thoth-agents 0.1.0 → 0.1.1

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.
@@ -4,7 +4,10 @@ interface TomlTable {
4
4
  [key: string]: TomlValue;
5
5
  }
6
6
  type TomlValue = TomlScalar | TomlArray | TomlTable;
7
- export type CodexTomlDocument = TomlTable;
7
+ declare const arrayTablePathsSymbol: unique symbol;
8
+ export type CodexTomlDocument = TomlTable & {
9
+ [arrayTablePathsSymbol]?: Set<string>;
10
+ };
8
11
  export interface CodexConfigMergeResult {
9
12
  success: boolean;
10
13
  configPath: string;
@@ -23,6 +26,7 @@ export declare function mergeCodexManagedConfig(document: CodexTomlDocument, opt
23
26
  diffSummary: string[];
24
27
  warnings: string[];
25
28
  };
29
+ export declare function patchCodexDefaultModeUserInputFeature(content: string): string;
26
30
  export declare function writeCodexConfigMerge(options: {
27
31
  configPath: string;
28
32
  dryRun?: boolean;
package/dist/cli/index.js CHANGED
@@ -2935,195 +2935,97 @@ import {
2935
2935
  writeFileSync as writeFileSync2
2936
2936
  } from "fs";
2937
2937
  import { dirname as dirname3 } from "path";
2938
- function isRecord3(value) {
2939
- return typeof value === "object" && value !== null && !Array.isArray(value);
2940
- }
2941
- function splitArrayItems(raw) {
2942
- const items = [];
2943
- let current = "";
2944
- let quote;
2945
- let escaped = false;
2946
- for (const char of raw) {
2947
- if (quote === "double" && escaped) {
2948
- current += char;
2949
- escaped = false;
2950
- continue;
2951
- }
2952
- if (quote === "double" && char === "\\") {
2953
- current += char;
2954
- escaped = true;
2955
- continue;
2956
- }
2957
- if (!quote && char === ",") {
2958
- items.push(current.trim());
2959
- current = "";
2960
- continue;
2961
- }
2962
- if (!quote && char === "'") quote = "single";
2963
- else if (quote === "single" && char === "'") quote = void 0;
2964
- else if (!quote && char === '"') quote = "double";
2965
- else if (quote === "double" && char === '"') quote = void 0;
2966
- current += char;
2967
- }
2968
- if (current.trim()) items.push(current.trim());
2969
- return items;
2970
- }
2971
- function parseBasicString(value) {
2972
- return value.slice(1, -1).replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
2973
- }
2974
- function parseScalar(raw) {
2975
- const value = raw.trim();
2976
- if (value === "true") return true;
2977
- if (value === "false") return false;
2978
- if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
2979
- if (value.startsWith("[") && value.endsWith("]")) {
2980
- const body = value.slice(1, -1).trim();
2981
- if (!body) return [];
2982
- return splitArrayItems(body).map(parseScalar);
2983
- }
2984
- if (value.startsWith('"') && value.endsWith('"')) {
2985
- return parseBasicString(value);
2986
- }
2987
- if (value.startsWith("'") && value.endsWith("'")) {
2988
- return value.slice(1, -1);
2989
- }
2990
- return value;
2991
- }
2992
- function parseTomlKeySegment(raw) {
2993
- const segment = raw.trim();
2994
- if (segment.startsWith('"') && segment.endsWith('"')) {
2995
- return parseBasicString(segment);
2996
- }
2997
- if (segment.startsWith("'") && segment.endsWith("'")) {
2998
- return segment.slice(1, -1);
2999
- }
3000
- return segment;
3001
- }
3002
- function parseTomlKeyPath(raw) {
3003
- const segments = [];
3004
- let current = "";
3005
- let quote;
3006
- let escaped = false;
3007
- for (const char of raw) {
3008
- if (quote === "double" && escaped) {
3009
- current += char;
3010
- escaped = false;
3011
- continue;
3012
- }
3013
- if (quote === "double" && char === "\\") {
3014
- current += char;
3015
- escaped = true;
3016
- continue;
3017
- }
3018
- if (!quote && char === ".") {
3019
- segments.push(parseTomlKeySegment(current));
3020
- current = "";
3021
- continue;
3022
- }
3023
- if (!quote && char === "'") quote = "single";
3024
- else if (quote === "single" && char === "'") quote = void 0;
3025
- else if (!quote && char === '"') quote = "double";
3026
- else if (quote === "double" && char === '"') quote = void 0;
3027
- current += char;
3028
- }
3029
- segments.push(parseTomlKeySegment(current));
3030
- return segments;
3031
- }
3032
- function ensureTable(root, path3) {
3033
- let current = root;
3034
- for (const segment of path3) {
3035
- const existing = current[segment];
3036
- if (!isRecord3(existing)) current[segment] = {};
3037
- current = current[segment];
3038
- }
3039
- return current;
3040
- }
3041
- function parseCodexToml(content) {
3042
- const root = {};
3043
- let table = [];
3044
- for (const rawLine of content.split(/\r?\n/)) {
3045
- const trimmed = rawLine.trim();
3046
- if (!trimmed || trimmed.startsWith("#")) continue;
3047
- const arrayTableMatch = /^\[\[([^\]]+)\]\]$/.exec(trimmed);
3048
- const tableMatch = /^\[([^\]]+)\]$/.exec(trimmed);
3049
- const tablePath = arrayTableMatch?.[1] ?? tableMatch?.[1];
3050
- if (tablePath) {
3051
- table = parseTomlKeyPath(tablePath);
3052
- ensureTable(root, table);
3053
- continue;
2938
+ function splitTomlLines(content) {
2939
+ return (content.match(/[^\r\n]*(?:\r\n|\n|\r)|[^\r\n]+$/g) ?? []).map(
2940
+ (rawLine) => {
2941
+ const eol = /(\r\n|\n|\r)$/.exec(rawLine)?.[1] ?? "";
2942
+ return {
2943
+ text: eol ? rawLine.slice(0, -eol.length) : rawLine,
2944
+ eol
2945
+ };
3054
2946
  }
3055
- const assignment = /^([^=]+)=(.*)$/.exec(trimmed);
3056
- if (!assignment) throw new Error(`Unsupported TOML line: ${rawLine}`);
3057
- ensureTable(root, table)[assignment[1].trim()] = parseScalar(assignment[2]);
3058
- }
3059
- return root;
2947
+ );
3060
2948
  }
3061
- function renderScalar2(value) {
3062
- if (typeof value === "string") {
3063
- const canUseLiteralString = value.includes("\\") && !value.includes("'") && [...value].every((char) => char.charCodeAt(0) >= 32);
3064
- if (canUseLiteralString) {
3065
- return `'${value}'`;
3066
- }
3067
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3068
- }
3069
- return String(value);
2949
+ function tomlLineBreak(content) {
2950
+ return /(\r\n|\n|\r)/.exec(content)?.[1] ?? "\n";
3070
2951
  }
3071
- function renderValue(value) {
3072
- if (Array.isArray(value)) return `[${value.map(renderScalar2).join(", ")}]`;
3073
- return renderScalar2(value);
2952
+ function uncommentedTomlLine(line) {
2953
+ return line.split("#", 1)[0].trim();
3074
2954
  }
3075
- function renderTomlKeySegment(segment) {
3076
- if (/^[A-Za-z0-9_]+$/.test(segment)) return segment;
3077
- const canUseLiteralKey = (segment.includes("\\") || segment.includes(".")) && !segment.includes("'") && [...segment].every((char) => char.charCodeAt(0) >= 32);
3078
- if (canUseLiteralKey) return `'${segment}'`;
3079
- return `"${segment.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
2955
+ function isTomlTableHeader(line) {
2956
+ const trimmed = uncommentedTomlLine(line);
2957
+ return trimmed.startsWith("[[") && trimmed.endsWith("]]") || trimmed.startsWith("[") && trimmed.endsWith("]");
3080
2958
  }
3081
- function renderTomlSection(lines, value, path3 = []) {
3082
- if (path3.length > 0)
3083
- lines.push(`[${path3.map(renderTomlKeySegment).join(".")}]`);
3084
- const nested = [];
3085
- for (const key of Object.keys(value).sort()) {
3086
- const entry = value[key];
3087
- if (isRecord3(entry)) nested.push([key, entry]);
3088
- else lines.push(`${key} = ${renderValue(entry)}`);
2959
+ function isFeaturesHeader(line) {
2960
+ return uncommentedTomlLine(line) === "[features]";
2961
+ }
2962
+ function renderTomlLines(lines) {
2963
+ return lines.map((line) => `${line.text}${line.eol}`).join("");
2964
+ }
2965
+ function ensureLineHasEol(lines, index, eol) {
2966
+ if (index >= 0 && lines[index]?.eol === "") lines[index].eol = eol;
2967
+ }
2968
+ function appendFeaturesSection(content) {
2969
+ const eol = tomlLineBreak(content);
2970
+ if (!content) {
2971
+ return `[features]${eol}default_mode_request_user_input = true${eol}`;
3089
2972
  }
3090
- if (path3.length > 0) lines.push("");
3091
- for (const [key, entry] of nested)
3092
- renderTomlSection(lines, entry, [...path3, key]);
2973
+ const separator = content.endsWith("\n") || content.endsWith("\r") ? content.endsWith(`${eol}${eol}`) ? "" : eol : `${eol}${eol}`;
2974
+ return `${content}${separator}[features]${eol}default_mode_request_user_input = true${eol}`;
3093
2975
  }
3094
- function renderCodexTomlDocument(document) {
3095
- const lines = [];
3096
- renderTomlSection(lines, document);
3097
- while (lines.at(-1) === "") lines.pop();
3098
- return `${lines.join("\n")}
3099
- `;
2976
+ function patchCodexDefaultModeUserInputFeature(content) {
2977
+ const lines = splitTomlLines(content);
2978
+ const eol = tomlLineBreak(content);
2979
+ const featuresStart = lines.findIndex((line) => isFeaturesHeader(line.text));
2980
+ if (featuresStart === -1) return appendFeaturesSection(content);
2981
+ let featuresEnd = lines.length;
2982
+ for (let index = featuresStart + 1; index < lines.length; index++) {
2983
+ if (isTomlTableHeader(lines[index].text)) {
2984
+ featuresEnd = index;
2985
+ break;
2986
+ }
2987
+ }
2988
+ const flagPattern = /^(\s*default_mode_request_user_input\s*=\s*)(true|false)(\b.*)$/;
2989
+ for (let index = featuresStart + 1; index < featuresEnd; index++) {
2990
+ const match = flagPattern.exec(lines[index].text);
2991
+ if (!match) continue;
2992
+ if (match[2] === "true") return content;
2993
+ lines[index].text = `${match[1]}true${match[3]}`;
2994
+ return renderTomlLines(lines);
2995
+ }
2996
+ let insertAt = featuresEnd;
2997
+ while (insertAt > featuresStart + 1 && lines[insertAt - 1].text.trim() === "")
2998
+ insertAt--;
2999
+ ensureLineHasEol(lines, insertAt - 1, eol);
3000
+ lines.splice(insertAt, 0, {
3001
+ text: "default_mode_request_user_input = true",
3002
+ eol
3003
+ });
3004
+ return renderTomlLines(lines);
3100
3005
  }
3101
- function mergeCodexManagedConfig(document, options) {
3102
- const features = ensureTable(document, ["features"]);
3103
- features.default_mode_request_user_input = true;
3104
- const diffSummary = ["ensure features.default_mode_request_user_input = true"];
3006
+ function buildCodexManagedConfigPatch(content, options) {
3007
+ const diffSummary = [
3008
+ "ensure features.default_mode_request_user_input = true"
3009
+ ];
3105
3010
  if (options.pluginId) {
3106
- const plugin = ensureTable(document, ["plugins", options.pluginId]);
3107
- plugin.enabled = true;
3108
- diffSummary.push(`ensure plugins."${options.pluginId}".enabled = true`);
3011
+ diffSummary.push(
3012
+ `plugin enablement for "${options.pluginId}" is not textually merged; use /plugins to enable it`
3013
+ );
3109
3014
  } else {
3110
3015
  diffSummary.push(
3111
3016
  "plugin enablement left to /plugins; no guessed plugin id written"
3112
3017
  );
3113
3018
  }
3114
3019
  return {
3115
- content: renderCodexTomlDocument(document),
3020
+ content: patchCodexDefaultModeUserInputFeature(content),
3116
3021
  diffSummary,
3117
- warnings: [
3118
- "Codex TOML comments and formatting may be rewritten; a backup is created before apply."
3119
- ]
3022
+ warnings: []
3120
3023
  };
3121
3024
  }
3122
3025
  function writeCodexConfigMerge(options) {
3123
3026
  try {
3124
3027
  const before = existsSync6(options.configPath) ? readFileSync5(options.configPath, "utf8") : "";
3125
- const parsed = parseCodexToml(before);
3126
- const merged = mergeCodexManagedConfig(parsed, {
3028
+ const merged = buildCodexManagedConfigPatch(before, {
3127
3029
  pluginId: options.pluginId
3128
3030
  });
3129
3031
  const changed = before !== merged.content;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",