@open-agent-toolkit/cli 0.3.36 → 0.3.38

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 (38) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/provider-sync/pilot-with-a-team.md +6 -6
  3. package/assets/docs/provider-sync/providers.md +1 -0
  4. package/assets/docs/reference/cli-reference.md +3 -3
  5. package/assets/docs/reference/project-artifacts.md +12 -3
  6. package/assets/docs/reference/what-oat-writes.md +14 -11
  7. package/assets/docs/skills/repo-improve.md +23 -19
  8. package/assets/docs/workflows/advanced/workflow-gates.md +7 -1
  9. package/assets/docs/workflows/projects/closeout/closeout-skills.md +3 -1
  10. package/assets/docs/workflows/projects/lifecycle.md +3 -1
  11. package/assets/public-package-versions.json +4 -4
  12. package/assets/skills/oat-project-brief/SKILL.md +1 -1
  13. package/assets/skills/oat-project-brief/references/brief-contract.md +32 -9
  14. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  15. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +14 -4
  16. package/assets/skills/oat-project-pr-progress/SKILL.md +23 -12
  17. package/assets/skills/oat-repo-knowledge-index/SKILL.md +226 -131
  18. package/dist/commands/project/closeout-check/index.d.ts.map +1 -1
  19. package/dist/commands/project/closeout-check/index.js +4 -8
  20. package/dist/commands/project/delivery/check.d.ts.map +1 -1
  21. package/dist/commands/project/delivery/check.js +32 -14
  22. package/dist/commands/project/delivery/evidence.d.ts +39 -0
  23. package/dist/commands/project/delivery/evidence.d.ts.map +1 -1
  24. package/dist/commands/project/delivery/evidence.js +163 -17
  25. package/dist/commands/project/delivery/refresh.d.ts.map +1 -1
  26. package/dist/commands/project/delivery/refresh.js +13 -16
  27. package/dist/commands/project/journal-check/index.d.ts +2 -0
  28. package/dist/commands/project/journal-check/index.d.ts.map +1 -1
  29. package/dist/commands/project/journal-check/index.js +78 -1
  30. package/dist/commands/project/journal-check/types.d.ts +1 -1
  31. package/dist/commands/project/journal-check/types.d.ts.map +1 -1
  32. package/dist/providers/codex/codec/config-merge.d.ts +5 -1
  33. package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
  34. package/dist/providers/codex/codec/config-merge.js +111 -62
  35. package/dist/providers/codex/codec/config-toml-edit.d.ts +8 -0
  36. package/dist/providers/codex/codec/config-toml-edit.d.ts.map +1 -0
  37. package/dist/providers/codex/codec/config-toml-edit.js +718 -0
  38. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"config-merge.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/config-merge.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,sBAAsB,EAAE,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA8FD,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,EAC/B,eAAe,EACf,YAAY,EACZ,iBAAsB,EACtB,iBAAiB,GAClB,EAAE,oBAAoB,GAAG,sBAAsB,CAgD/C;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,EACtC,eAAe,EACf,IAAI,EACJ,iBAAsB,EACtB,iBAAiB,GAClB,EAAE,8BAA8B,GAAG,sBAAsB,CAOzD"}
1
+ {"version":3,"file":"config-merge.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/config-merge.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,sBAAsB,EAAE,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAqFD,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,eAAe,EACf,YAAY,EACZ,iBAAsB,EACtB,iBAAiB,GAClB,EAAE,oBAAoB,GAAG,sBAAsB,CAkH/C;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,EACtC,eAAe,EACf,IAAI,EACJ,iBAAsB,EACtB,iBAAiB,GAClB,EAAE,8BAA8B,GAAG,sBAAsB,CAOzD"}
@@ -1,44 +1,6 @@
1
1
  import { CliError } from '../../../errors/index.js';
2
2
  import TOML from '@iarna/toml';
3
- import { stringifyToml } from './shared.js';
4
- function isEscapedBasicStringDelimiter(line, index) {
5
- let backslashCount = 0;
6
- for (let cursor = index - 1; cursor >= 0 && line[cursor] === '\\'; cursor--) {
7
- backslashCount += 1;
8
- }
9
- return backslashCount % 2 === 1;
10
- }
11
- function scanMultilineStringDelimiter(line, initialDelimiter) {
12
- let delimiter = initialDelimiter;
13
- for (let index = 0; index < line.length - 2; index++) {
14
- if (delimiter === null) {
15
- const candidate = line.slice(index, index + 3);
16
- if ((candidate === '"""' && !isEscapedBasicStringDelimiter(line, index)) ||
17
- candidate === "'''") {
18
- delimiter = candidate;
19
- index += 2;
20
- }
21
- continue;
22
- }
23
- if (line.startsWith(delimiter, index) &&
24
- (delimiter === "'''" || !isEscapedBasicStringDelimiter(line, index))) {
25
- delimiter = null;
26
- index += 2;
27
- }
28
- }
29
- return delimiter;
30
- }
31
- function normalizeCodexConfigIndentation(content) {
32
- let multilineDelimiter = null;
33
- return content
34
- .split('\n')
35
- .map((line) => {
36
- const normalizedLine = multilineDelimiter === null ? line.trimStart() : line;
37
- multilineDelimiter = scanMultilineStringDelimiter(normalizedLine, multilineDelimiter);
38
- return normalizedLine;
39
- })
40
- .join('\n');
41
- }
3
+ import { removeTomlTable, renderTomlString, setTomlValue, } from './config-toml-edit.js';
42
4
  function parseConfig(content) {
43
5
  if (!content || content.trim() === '') {
44
6
  return {};
@@ -57,39 +19,85 @@ function parseConfig(content) {
57
19
  throw new CliError(`Failed to parse .codex/config.toml: ${error instanceof Error ? error.message : 'unknown parse error'}`);
58
20
  }
59
21
  }
22
+ function isTable(value) {
23
+ return (!!value &&
24
+ typeof value === 'object' &&
25
+ !Array.isArray(value) &&
26
+ !(value instanceof Date));
27
+ }
60
28
  function getObject(value) {
61
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
29
+ return isTable(value) ? { ...value } : {};
30
+ }
31
+ function requireTableOrAbsent(value, label) {
32
+ if (value === undefined) {
62
33
  return {};
63
34
  }
64
- return { ...value };
35
+ if (!isTable(value)) {
36
+ throw new CliError(`Refusing to rewrite .codex/config.toml: ${label} must be a table. Fix it by hand and rerun sync.`);
37
+ }
38
+ return value;
39
+ }
40
+ function requireNotTable(value, label) {
41
+ if (isTable(value)) {
42
+ throw new CliError(`Refusing to rewrite .codex/config.toml: ${label} must not be a table. Fix it by hand and rerun sync.`);
43
+ }
65
44
  }
66
45
  function getFiniteNumber(value) {
67
46
  return typeof value === 'number' && Number.isFinite(value) ? value : null;
68
47
  }
48
+ function canonicalTomlValue(value) {
49
+ if (value instanceof Date) {
50
+ return { $date: value.toISOString() };
51
+ }
52
+ if (Array.isArray(value)) {
53
+ return value.map(canonicalTomlValue);
54
+ }
55
+ if (typeof value === 'bigint') {
56
+ return { $bigint: value.toString() };
57
+ }
58
+ if (typeof value === 'number' && !Number.isFinite(value)) {
59
+ return { $number: String(value) };
60
+ }
61
+ if (isTable(value)) {
62
+ return Object.fromEntries(Object.keys(value)
63
+ .sort()
64
+ .map((key) => [key, canonicalTomlValue(value[key])]));
65
+ }
66
+ return value;
67
+ }
69
68
  /** Reads a valid numeric agents.max_depth value from Codex TOML content. */
70
69
  export function readCodexMaxDepth(content) {
71
70
  const parsed = parseConfig(content);
72
71
  const agents = getObject(parsed.agents);
73
72
  return getFiniteNumber(agents.max_depth);
74
73
  }
75
- /** Merges managed Codex roles and required shared agent configuration. */
74
+ /**
75
+ * Merges managed Codex roles and required shared agent configuration in place:
76
+ * other tools keep ownership markers in this file's comments, so unmanaged
77
+ * content must survive byte for byte.
78
+ */
76
79
  export function mergeCodexConfig({ existingContent, desiredRoles, staleManagedRoles = [], inheritedMaxDepth, }) {
77
80
  const parsed = parseConfig(existingContent);
78
- const features = getObject(parsed.features);
79
- const agents = getObject(parsed.agents);
80
- const targetMaxDepth = getFiniteNumber(agents.max_depth);
81
- const validInheritedMaxDepth = getFiniteNumber(inheritedMaxDepth);
82
- agents.max_depth = Math.max(2, targetMaxDepth ?? 2, validInheritedMaxDepth ?? 2);
83
- const nextConfig = {
84
- ...parsed,
85
- features: {
86
- ...features,
87
- multi_agent: true,
88
- },
89
- agents,
90
- };
91
- for (const role of desiredRoles) {
92
- agents[role.roleName] = {
81
+ const features = requireTableOrAbsent(parsed.features, 'features');
82
+ const agents = requireTableOrAbsent(parsed.agents, 'agents');
83
+ const roles = new Map(desiredRoles.map((role) => [role.roleName, role]));
84
+ const stale = new Set(staleManagedRoles);
85
+ requireNotTable(features.multi_agent, 'features.multi_agent');
86
+ if (!stale.has('max_depth')) {
87
+ requireNotTable(agents.max_depth, 'agents.max_depth');
88
+ }
89
+ for (const roleName of roles.keys()) {
90
+ if (stale.has(roleName)) {
91
+ continue;
92
+ }
93
+ const role = requireTableOrAbsent(agents[roleName], `agents.${roleName}`);
94
+ requireNotTable(role.description, `agents.${roleName}.description`);
95
+ requireNotTable(role.config_file, `agents.${roleName}.config_file`);
96
+ }
97
+ const maxDepth = Math.max(2, getFiniteNumber(agents.max_depth) ?? 2, getFiniteNumber(inheritedMaxDepth) ?? 2);
98
+ const expectedAgents = { ...agents, max_depth: maxDepth };
99
+ for (const role of roles.values()) {
100
+ expectedAgents[role.roleName] = {
93
101
  ...getObject(agents[role.roleName]),
94
102
  description: role.description,
95
103
  config_file: role.configFile,
@@ -97,16 +105,57 @@ export function mergeCodexConfig({ existingContent, desiredRoles, staleManagedRo
97
105
  }
98
106
  const removedRoles = [];
99
107
  for (const staleRole of staleManagedRoles) {
100
- if (staleRole in agents) {
101
- delete agents[staleRole];
108
+ if (Object.hasOwn(expectedAgents, staleRole)) {
109
+ delete expectedAgents[staleRole];
102
110
  removedRoles.push(staleRole);
103
111
  }
104
112
  }
105
- const mergedContent = normalizeCodexConfigIndentation(stringifyToml(nextConfig));
106
- const changed = (existingContent ?? '').trimEnd() !== mergedContent.trimEnd();
113
+ const removed = new Set(removedRoles);
114
+ const original = existingContent !== null && existingContent.trim() !== ''
115
+ ? existingContent
116
+ : '';
117
+ let mergedContent = original;
118
+ if (features.multi_agent !== true) {
119
+ mergedContent = setTomlValue(mergedContent, ['features', 'multi_agent'], 'true');
120
+ }
121
+ if (agents.max_depth !== maxDepth && !removed.has('max_depth')) {
122
+ mergedContent = setTomlValue(mergedContent, ['agents', 'max_depth'], String(maxDepth));
123
+ }
124
+ for (const staleRole of removedRoles) {
125
+ mergedContent = removeTomlTable(mergedContent, ['agents', staleRole]);
126
+ }
127
+ for (const role of roles.values()) {
128
+ if (removed.has(role.roleName)) {
129
+ continue;
130
+ }
131
+ const current = getObject(agents[role.roleName]);
132
+ if (current.description !== role.description) {
133
+ mergedContent = setTomlValue(mergedContent, ['agents', role.roleName, 'description'], renderTomlString(role.description));
134
+ }
135
+ if (current.config_file !== role.configFile) {
136
+ mergedContent = setTomlValue(mergedContent, ['agents', role.roleName, 'config_file'], renderTomlString(role.configFile));
137
+ }
138
+ }
139
+ const expected = {
140
+ ...parsed,
141
+ features: { ...features, multi_agent: true },
142
+ agents: expectedAgents,
143
+ };
144
+ let actual;
145
+ try {
146
+ actual = parseConfig(mergedContent);
147
+ }
148
+ catch {
149
+ actual = null;
150
+ }
151
+ if (actual === null ||
152
+ JSON.stringify(canonicalTomlValue(actual)) !==
153
+ JSON.stringify(canonicalTomlValue(expected))) {
154
+ throw new CliError('Refusing to write .codex/config.toml: the in-place edit did not produce the expected managed configuration. Edit the file by hand and rerun sync.');
155
+ }
107
156
  return {
108
157
  mergedContent,
109
- changed,
158
+ changed: mergedContent !== (existingContent ?? ''),
110
159
  removedRoles,
111
160
  };
112
161
  }
@@ -0,0 +1,8 @@
1
+ type KeyPath = readonly string[];
2
+ /** Renders a TOML basic string; JSON escaping is not TOML-safe for all input. */
3
+ export declare function renderTomlString(value: string): string;
4
+ /** `renderedValue` must already be a valid TOML value literal. */
5
+ export declare function setTomlValue(text: string, path: KeyPath, renderedValue: string): string;
6
+ export declare function removeTomlTable(text: string, path: KeyPath): string;
7
+ export {};
8
+ //# sourceMappingURL=config-toml-edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-toml-edit.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/config-toml-edit.ts"],"names":[],"mappings":"AAOA,KAAK,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC;AAofjC,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAyBtD;AAqTD,kEAAkE;AAClE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,aAAa,EAAE,MAAM,GACpB,MAAM,CAsFR;AA8BD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CA4BnE"}