@open-agent-toolkit/cli 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/assets/agents/oat-codebase-mapper.md +3 -2
- package/assets/agents/oat-reviewer.md +5 -13
- package/assets/agents/skeptical-evaluator.md +6 -2
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/backlog-lifecycle.md +3 -2
- package/assets/docs/cli-utilities/bootstrap.md +5 -2
- package/assets/docs/cli-utilities/config-and-local-state.md +13 -6
- package/assets/docs/cli-utilities/configuration.md +25 -10
- package/assets/docs/cli-utilities/tool-packs.md +60 -10
- package/assets/docs/cli-utilities/workflow-gates.md +37 -0
- package/assets/docs/contributing/code.md +9 -5
- package/assets/docs/provider-sync/commands.md +17 -8
- package/assets/docs/provider-sync/instruction-sync.md +241 -39
- package/assets/docs/provider-sync/providers.md +20 -0
- package/assets/docs/provider-sync/scope-and-surface.md +3 -1
- package/assets/docs/reference/cli-reference.md +4 -1
- package/assets/docs/reference/oat-directory-structure.md +12 -11
- package/assets/docs/reference/troubleshooting.md +19 -4
- package/assets/docs/workflows/projects/evidence-layers.md +5 -1
- package/assets/docs/workflows/projects/implementation-execution.md +22 -8
- package/assets/docs/workflows/projects/lifecycle.md +4 -6
- package/assets/docs/workflows/projects/retro.md +7 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-agent-instructions-analyze/SKILL.md +4 -4
- package/assets/skills/oat-agent-instructions-analyze/references/analysis-artifact-template.md +5 -5
- package/assets/skills/oat-agent-instructions-analyze/references/docs/rules-files.md +14 -11
- package/assets/skills/oat-agent-instructions-analyze/references/quality-checklist.md +2 -2
- package/assets/skills/oat-agent-instructions-analyze/scripts/resolve-instruction-files.sh +4 -3
- package/assets/skills/oat-agent-instructions-analyze/scripts/resolve-providers.sh +17 -6
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +3 -3
- package/assets/skills/oat-agent-instructions-apply/references/docs/rules-files.md +14 -11
- package/assets/skills/oat-dispatch-subagents/SKILL.md +1 -1
- package/assets/skills/oat-dispatch-subagents/references/managed-claude-example.json +387 -0
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +75 -0
- package/assets/skills/oat-doctor/SKILL.md +17 -13
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/SKILL.md +2 -2
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +33 -11
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +24 -22
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/references/phase-execution.md +109 -8
- package/assets/skills/oat-project-lite/SKILL.md +23 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-next/SKILL.md +3 -3
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-progress/SKILL.md +2 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-retro/SKILL.md +70 -6
- package/assets/skills/oat-project-review-provide/SKILL.md +3 -1
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +3 -1
- package/assets/templates/docs-app-mkdocs/docs/contributing.md +1 -3
- package/assets/templates/pjm-agents.md +7 -3
- package/assets/templates/state.md +2 -2
- package/dist/agents/canonical/resolve.d.ts +6 -4
- package/dist/agents/canonical/resolve.d.ts.map +1 -1
- package/dist/agents/canonical/resolve.js +6 -4
- package/dist/commands/backlog/archive.d.ts +10 -2
- package/dist/commands/backlog/archive.d.ts.map +1 -1
- package/dist/commands/backlog/archive.js +22 -6
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +5 -1
- package/dist/commands/backlog/rewrite-references.d.ts +41 -0
- package/dist/commands/backlog/rewrite-references.d.ts.map +1 -0
- package/dist/commands/backlog/rewrite-references.js +516 -0
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +69 -34
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +51 -2
- package/dist/commands/gate/review-verdict.d.ts +22 -0
- package/dist/commands/gate/review-verdict.d.ts.map +1 -1
- package/dist/commands/gate/review-verdict.js +111 -0
- package/dist/commands/init/index.d.ts +6 -0
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +27 -4
- package/dist/commands/init/tools/index.d.ts +18 -1
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +84 -17
- package/dist/commands/init/tools/project-guidance.d.ts +19 -4
- package/dist/commands/init/tools/project-guidance.d.ts.map +1 -1
- package/dist/commands/init/tools/project-guidance.js +58 -15
- package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/index.js +5 -1
- package/dist/commands/instructions/instructions.types.d.ts +105 -4
- package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.types.js +4 -5
- package/dist/commands/instructions/instructions.utils.d.ts +161 -6
- package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.js +650 -12
- package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
- package/dist/commands/instructions/sync/sync.js +256 -18
- package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
- package/dist/commands/instructions/validate/validate.js +36 -8
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +19 -4
- package/dist/commands/pjm/init.d.ts +3 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +23 -5
- package/dist/commands/project/dispatch/canonical-role.d.ts +33 -0
- package/dist/commands/project/dispatch/canonical-role.d.ts.map +1 -0
- package/dist/commands/project/dispatch/canonical-role.js +114 -0
- package/dist/commands/project/dispatch/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch/index.js +3 -1
- package/dist/commands/project/dispatch/record.d.ts.map +1 -1
- package/dist/commands/project/dispatch/record.js +146 -4
- package/dist/commands/shared/agents-md.d.ts +26 -5
- package/dist/commands/shared/agents-md.d.ts.map +1 -1
- package/dist/commands/shared/agents-md.js +182 -3
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +28 -21
- package/dist/commands/tools/guidance/index.d.ts +15 -0
- package/dist/commands/tools/guidance/index.d.ts.map +1 -0
- package/dist/commands/tools/guidance/index.js +81 -0
- package/dist/commands/tools/index.d.ts.map +1 -1
- package/dist/commands/tools/index.js +2 -0
- package/dist/commands/tools/shared/sync-evidence.d.ts.map +1 -1
- package/dist/commands/tools/shared/sync-evidence.js +32 -5
- package/dist/config/oat-config.d.ts +57 -3
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +223 -15
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +12 -2
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +31 -3
- package/dist/providers/claude/dispatch-envelope.d.ts +53 -0
- package/dist/providers/claude/dispatch-envelope.d.ts.map +1 -1
- package/dist/providers/claude/dispatch-envelope.js +324 -48
- package/dist/providers/claude/rule-transform.js +1 -1
- package/dist/providers/copilot/rule-transform.js +1 -1
- package/dist/providers/cursor/rule-transform.js +1 -1
- package/dist/providers/identity/absolute-paths.d.ts +13 -0
- package/dist/providers/identity/absolute-paths.d.ts.map +1 -1
- package/dist/providers/identity/absolute-paths.js +27 -14
- package/dist/providers/identity/generic-dispatch-record.d.ts +212 -0
- package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -1
- package/dist/providers/identity/generic-dispatch-record.js +78 -19
- package/dist/providers/identity/oat-dispatch-record.d.ts +5 -0
- package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -1
- package/dist/providers/identity/oat-dispatch-record.js +11 -2
- package/dist/providers/shared/registry.d.ts +6 -0
- package/dist/providers/shared/registry.d.ts.map +1 -1
- package/dist/providers/shared/registry.js +30 -0
- package/dist/rules/canonical/parse.d.ts.map +1 -1
- package/dist/rules/canonical/parse.js +38 -3
- package/package.json +2 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Result of {@link rewriteInboundReferences}. */
|
|
2
|
+
export interface RewriteInboundReferencesResult {
|
|
3
|
+
/** Files whose content was rewritten, relative to the repository root. */
|
|
4
|
+
rewritten: string[];
|
|
5
|
+
/** One warning per reference that names the item but cannot be resolved. */
|
|
6
|
+
warnings: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Options for {@link rewriteInboundReferences}. */
|
|
9
|
+
export interface RewriteInboundReferencesOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Rebase the moved item's own relative links from `items/` to `archived/`.
|
|
12
|
+
* Defaults to `true`; a retry on an already-archived item passes `false`
|
|
13
|
+
* because its links were rebased (or written) against `archived/` already.
|
|
14
|
+
*/
|
|
15
|
+
rebaseMovedItem?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** A Markdown segment: prose, an inline code span, or a fenced code block. */
|
|
18
|
+
export interface MarkdownSegment {
|
|
19
|
+
kind: 'prose' | 'span' | 'fence';
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Split Markdown into prose, inline code spans, and fenced code blocks.
|
|
24
|
+
* Fences (``` or ~~~) close on a same-character run at least as long, or run
|
|
25
|
+
* to the end of the file. Inline code spans pair a backtick run with the next
|
|
26
|
+
* run of the same length inside one paragraph (a blank line ends it), so a
|
|
27
|
+
* stray backtick never swallows later paragraphs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function splitMarkdownCode(content: string): MarkdownSegment[];
|
|
30
|
+
/**
|
|
31
|
+
* After `items/<id>.md` moved to `archived/<id>.md`, rewrite every reference
|
|
32
|
+
* to the old path in Markdown under `.oat/repo/**` (links and repository-root
|
|
33
|
+
* path strings such as `oat_external_plan_sources` frontmatter). An inline code
|
|
34
|
+
* span is rewritten only when its whole content is a path to the item (the
|
|
35
|
+
* external-plan citation form); other spans and fenced code are left as
|
|
36
|
+
* written with a warning, so a recorded command keeps its meaning. References that name the item's `items/` path
|
|
37
|
+
* but cannot be resolved are reported as warnings and left untouched. The
|
|
38
|
+
* rewrite is idempotent, so a re-run on an already-archived item is safe.
|
|
39
|
+
*/
|
|
40
|
+
export declare function rewriteInboundReferences(backlogRoot: string, itemsPath: string, archivedPath: string, options?: RewriteInboundReferencesOptions): Promise<RewriteInboundReferencesResult>;
|
|
41
|
+
//# sourceMappingURL=rewrite-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewrite-references.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/rewrite-references.ts"],"names":[],"mappings":"AAsCA,kDAAkD;AAClD,MAAM,WAAW,8BAA8B;IAC7C,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA0RD,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0EpE;AAmND;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,8BAA8B,CAAC,CAsCzC"}
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { constants as fsConstants, lstatSync } from 'node:fs';
|
|
4
|
+
import { lstat, open as openFile, readdir, realpath, rename, unlink, } from 'node:fs/promises';
|
|
5
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
/** Characters that end a path-like token when scanning Markdown text. */
|
|
9
|
+
const TOKEN_DELIMITERS = new Set([
|
|
10
|
+
' ',
|
|
11
|
+
'\t',
|
|
12
|
+
'\n',
|
|
13
|
+
'\r',
|
|
14
|
+
'(',
|
|
15
|
+
')',
|
|
16
|
+
'[',
|
|
17
|
+
']',
|
|
18
|
+
'<',
|
|
19
|
+
'>',
|
|
20
|
+
'"',
|
|
21
|
+
"'",
|
|
22
|
+
'`',
|
|
23
|
+
'|',
|
|
24
|
+
',',
|
|
25
|
+
'=',
|
|
26
|
+
]);
|
|
27
|
+
const SKIPPED_DIRECTORIES = new Set(['.git', 'node_modules']);
|
|
28
|
+
function toPosix(path) {
|
|
29
|
+
return path.split(sep).join('/');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Derive the scan layout from the backlog root. The canonical backlog lives at
|
|
33
|
+
* `<repo>/.oat/repo/pjm/backlog`; a non-canonical `--backlog-root` scans only
|
|
34
|
+
* the backlog root itself.
|
|
35
|
+
*/
|
|
36
|
+
function resolveLayout(backlogRoot) {
|
|
37
|
+
const backlog = resolve(backlogRoot);
|
|
38
|
+
const pjm = dirname(backlog);
|
|
39
|
+
if (basename(backlog) !== 'backlog' || basename(pjm) !== 'pjm') {
|
|
40
|
+
return { scanRoot: backlog, repoRoot: null };
|
|
41
|
+
}
|
|
42
|
+
const scanRoot = dirname(pjm);
|
|
43
|
+
const oatDir = dirname(scanRoot);
|
|
44
|
+
const repoRoot = basename(scanRoot) === 'repo' && basename(oatDir) === '.oat'
|
|
45
|
+
? dirname(oatDir)
|
|
46
|
+
: null;
|
|
47
|
+
return { scanRoot, repoRoot };
|
|
48
|
+
}
|
|
49
|
+
async function isInsideGitWorkTree(cwd) {
|
|
50
|
+
try {
|
|
51
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], { cwd });
|
|
52
|
+
return stdout.trim() === 'true';
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* True only for a regular file (never a symlink) whose real path stays inside
|
|
60
|
+
* `realScanRoot`, so the scan can never read or write outside `.oat/repo`.
|
|
61
|
+
*/
|
|
62
|
+
async function isRegularFileInside(path, realScanRoot) {
|
|
63
|
+
try {
|
|
64
|
+
if (!(await lstat(path)).isFile()) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const real = await realpath(path);
|
|
68
|
+
return real === realScanRoot || real.startsWith(`${realScanRoot}${sep}`);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** True when anything (file, directory, or link) exists at `path`. */
|
|
75
|
+
function pathExistsSync(path) {
|
|
76
|
+
try {
|
|
77
|
+
lstatSync(path);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** `O_NOFOLLOW` where the platform has it: refuse to open through a symlink. */
|
|
85
|
+
const NO_FOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
86
|
+
async function readNoFollow(path) {
|
|
87
|
+
const handle = await openFile(path, fsConstants.O_RDONLY | NO_FOLLOW);
|
|
88
|
+
try {
|
|
89
|
+
const info = await handle.stat();
|
|
90
|
+
return {
|
|
91
|
+
content: await handle.readFile('utf8'),
|
|
92
|
+
identity: { dev: info.dev, ino: info.ino, mode: info.mode },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await handle.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Replace `path` with `content` without ever writing to its existing inode:
|
|
101
|
+
* write a temporary file in the same (already verified, in-tree) directory,
|
|
102
|
+
* then rename it over the original. Truncating in place would write through
|
|
103
|
+
* every other hard link to the same inode, including one outside `.oat/repo`.
|
|
104
|
+
* Immediately before the rename, the target must still be the regular,
|
|
105
|
+
* non-symlink file that was read (same device and inode); otherwise the
|
|
106
|
+
* replacement is abandoned. The original permission bits are preserved.
|
|
107
|
+
*/
|
|
108
|
+
async function replaceAtomically(path, content, identity) {
|
|
109
|
+
const temporary = join(dirname(path), `.${basename(path)}.oat-rewrite-${process.pid}-${randomBytes(6).toString('hex')}.tmp`);
|
|
110
|
+
const permissions = identity.mode & 0o7777;
|
|
111
|
+
const handle = await openFile(temporary, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | NO_FOLLOW, permissions);
|
|
112
|
+
try {
|
|
113
|
+
try {
|
|
114
|
+
await handle.writeFile(content, 'utf8');
|
|
115
|
+
// The creation mode is masked by the umask; set the exact bits.
|
|
116
|
+
await handle.chmod(permissions);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await handle.close();
|
|
120
|
+
}
|
|
121
|
+
const current = await lstat(path);
|
|
122
|
+
if (!current.isFile() ||
|
|
123
|
+
current.dev !== identity.dev ||
|
|
124
|
+
current.ino !== identity.ino) {
|
|
125
|
+
throw new Error(`${path} changed while its references were being rewritten; it was left untouched. Re-run \`oat backlog archive\` to retry.`);
|
|
126
|
+
}
|
|
127
|
+
await rename(temporary, path);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
await unlink(temporary).catch(() => undefined);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function walkMarkdown(directory) {
|
|
135
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
136
|
+
const files = [];
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
const path = join(directory, entry.name);
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
if (!SKIPPED_DIRECTORIES.has(entry.name)) {
|
|
141
|
+
files.push(...(await walkMarkdown(path)));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
145
|
+
files.push(path);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return files;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* List Markdown files under the scan root: tracked plus untracked-but-not-
|
|
152
|
+
* ignored files inside a Git work tree, or a filesystem walk outside Git.
|
|
153
|
+
*/
|
|
154
|
+
async function listMarkdownFiles(scanRoot) {
|
|
155
|
+
const realScanRoot = await realpath(scanRoot);
|
|
156
|
+
if (await isInsideGitWorkTree(scanRoot)) {
|
|
157
|
+
const { stdout } = await execFileAsync('git', [
|
|
158
|
+
'ls-files',
|
|
159
|
+
'-z',
|
|
160
|
+
'--cached',
|
|
161
|
+
'--others',
|
|
162
|
+
'--exclude-standard',
|
|
163
|
+
'--',
|
|
164
|
+
'*.md',
|
|
165
|
+
], { cwd: scanRoot, maxBuffer: 64 * 1024 * 1024 });
|
|
166
|
+
const unique = [...new Set(stdout.split('\0').filter(Boolean))];
|
|
167
|
+
const files = [];
|
|
168
|
+
for (const relativePath of unique) {
|
|
169
|
+
const path = join(scanRoot, relativePath);
|
|
170
|
+
if (await isRegularFileInside(path, realScanRoot)) {
|
|
171
|
+
files.push(path);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return files;
|
|
175
|
+
}
|
|
176
|
+
const walked = [];
|
|
177
|
+
for (const path of await walkMarkdown(scanRoot)) {
|
|
178
|
+
if (await isRegularFileInside(path, realScanRoot)) {
|
|
179
|
+
walked.push(path);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return walked;
|
|
183
|
+
}
|
|
184
|
+
function isRelativeTarget(target) {
|
|
185
|
+
return (target.length > 0 &&
|
|
186
|
+
!target.startsWith('/') &&
|
|
187
|
+
!target.startsWith('#') &&
|
|
188
|
+
!/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(target));
|
|
189
|
+
}
|
|
190
|
+
/** Render `absolute` relative to `fromDirectory`, keeping a leading `./` style. */
|
|
191
|
+
function relativeFrom(fromDirectory, absolute, original) {
|
|
192
|
+
const next = toPosix(relative(fromDirectory, absolute));
|
|
193
|
+
return original.startsWith('./') && !next.startsWith('..')
|
|
194
|
+
? `./${next}`
|
|
195
|
+
: next;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Rewrite one path token that ends in `<id>.md`. Returns the replacement, or
|
|
199
|
+
* `null` when the token does not refer to the moved item.
|
|
200
|
+
*/
|
|
201
|
+
function rewriteItemToken(token, context) {
|
|
202
|
+
const { layout, itemsPath, archivedPath } = context;
|
|
203
|
+
const itemFile = basename(itemsPath);
|
|
204
|
+
if (isRelativeTarget(token)) {
|
|
205
|
+
const fromFile = resolve(context.baseDirectory, token);
|
|
206
|
+
if (fromFile === itemsPath) {
|
|
207
|
+
return relativeFrom(context.currentDirectory, archivedPath, token);
|
|
208
|
+
}
|
|
209
|
+
// A token that already resolves to a different, existing file is a
|
|
210
|
+
// working link: never repoint it through a fallback base.
|
|
211
|
+
if (pathExistsSync(fromFile)) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
for (const base of [layout.repoRoot, layout.scanRoot]) {
|
|
215
|
+
if (base !== null && resolve(base, token) === itemsPath) {
|
|
216
|
+
return toPosix(relative(base, archivedPath));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Only a token that resolves to the former items/ path is rewritten. URLs
|
|
221
|
+
// (any scheme) are never touched or reported; an unresolved local form that
|
|
222
|
+
// still names `items/<id>.md` is reported for a manual fix. Only
|
|
223
|
+
// `/`-separated forms are recognized: the token scan requires `/` or a
|
|
224
|
+
// delimiter before the file name, so backslash paths never reach here.
|
|
225
|
+
const itemsSuffix = `items/${itemFile}`;
|
|
226
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(token)) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (token === itemsSuffix || token.endsWith(`/${itemsSuffix}`)) {
|
|
230
|
+
context.warnings.push(`${context.fileName} references \`${token}\`, which names archived backlog item ${basename(itemFile, '.md')} but does not resolve to its old items/ path; update it by hand.`);
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Split Markdown into prose, inline code spans, and fenced code blocks.
|
|
236
|
+
* Fences (``` or ~~~) close on a same-character run at least as long, or run
|
|
237
|
+
* to the end of the file. Inline code spans pair a backtick run with the next
|
|
238
|
+
* run of the same length inside one paragraph (a blank line ends it), so a
|
|
239
|
+
* stray backtick never swallows later paragraphs.
|
|
240
|
+
*/
|
|
241
|
+
export function splitMarkdownCode(content) {
|
|
242
|
+
const segments = [];
|
|
243
|
+
const push = (kind, text) => {
|
|
244
|
+
if (text.length === 0) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const last = segments.at(-1);
|
|
248
|
+
if (last && last.kind === kind && kind === 'prose') {
|
|
249
|
+
last.text += text;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
segments.push({ kind, text });
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const pushParagraph = (text) => {
|
|
256
|
+
let cursor = 0;
|
|
257
|
+
const opener = /`+/gu;
|
|
258
|
+
let match;
|
|
259
|
+
while ((match = opener.exec(text)) !== null) {
|
|
260
|
+
const run = match[0];
|
|
261
|
+
const closer = new RegExp(`(?<!\`)${run}(?!\`)`, 'u');
|
|
262
|
+
const rest = text.slice(match.index + run.length);
|
|
263
|
+
const close = closer.exec(rest);
|
|
264
|
+
if (!close) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const end = match.index + run.length + close.index + run.length;
|
|
268
|
+
push('prose', text.slice(cursor, match.index));
|
|
269
|
+
push('span', text.slice(match.index, end));
|
|
270
|
+
cursor = end;
|
|
271
|
+
opener.lastIndex = end;
|
|
272
|
+
}
|
|
273
|
+
push('prose', text.slice(cursor));
|
|
274
|
+
};
|
|
275
|
+
const pushProse = (text) => {
|
|
276
|
+
// Blank lines end a paragraph; the separators stay prose.
|
|
277
|
+
for (const part of text.split(/(\r?\n[ \t]*\r?\n)/u)) {
|
|
278
|
+
pushParagraph(part);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const lines = content.split(/(?<=\n)/u);
|
|
282
|
+
let prose = '';
|
|
283
|
+
let fence = null;
|
|
284
|
+
let code = '';
|
|
285
|
+
for (const line of lines) {
|
|
286
|
+
if (fence) {
|
|
287
|
+
code += line;
|
|
288
|
+
const close = /^ {0,3}(`{3,}|~{3,})[ \t]*\r?\n?$/u.exec(line);
|
|
289
|
+
if (close &&
|
|
290
|
+
close[1][0] === fence.char &&
|
|
291
|
+
close[1].length >= fence.length) {
|
|
292
|
+
push('fence', code);
|
|
293
|
+
code = '';
|
|
294
|
+
fence = null;
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const open = /^ {0,3}(`{3,}|~{3,})/u.exec(line);
|
|
299
|
+
if (open) {
|
|
300
|
+
pushProse(prose);
|
|
301
|
+
prose = '';
|
|
302
|
+
fence = { char: open[1][0], length: open[1].length };
|
|
303
|
+
code = line;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
prose += line;
|
|
307
|
+
}
|
|
308
|
+
pushProse(prose);
|
|
309
|
+
push('fence', code);
|
|
310
|
+
return segments;
|
|
311
|
+
}
|
|
312
|
+
/** Report every `items/<id>.md` occurrence left inside code as written. */
|
|
313
|
+
function warnCodeOccurrences(text, context) {
|
|
314
|
+
const itemFile = basename(context.itemsPath);
|
|
315
|
+
const needle = `items/${itemFile}`;
|
|
316
|
+
let index = text.indexOf(needle);
|
|
317
|
+
while (index !== -1) {
|
|
318
|
+
const after = text[index + needle.length] ?? '';
|
|
319
|
+
if (after === '' || !/[A-Za-z0-9_-]/u.test(after)) {
|
|
320
|
+
context.warnings.push(`${context.fileName} mentions \`${needle}\` inside code, which is left as written; update it by hand if it is a live reference to archived backlog item ${basename(itemFile, '.md')}.`);
|
|
321
|
+
}
|
|
322
|
+
index = text.indexOf(needle, index + needle.length);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Rewrite an inline code span whose whole content is one path (optionally
|
|
327
|
+
* with a `#anchor`) that resolves to the moved item — the external-plan
|
|
328
|
+
* "Source artifact or scope" citation form. Any other span naming the item is
|
|
329
|
+
* left as written with a warning, so recorded commands keep their meaning.
|
|
330
|
+
*/
|
|
331
|
+
function rewriteCodeSpan(span, context) {
|
|
332
|
+
const run = /^`+/u.exec(span)[0];
|
|
333
|
+
const inner = span.slice(run.length, span.length - run.length);
|
|
334
|
+
const single = /^(\s*)([^\s`]+)(\s*)$/u.exec(inner);
|
|
335
|
+
if (single) {
|
|
336
|
+
const [, leading, token, trailing] = single;
|
|
337
|
+
const hash = token.indexOf('#');
|
|
338
|
+
const path = hash === -1 ? token : token.slice(0, hash);
|
|
339
|
+
const anchor = hash === -1 ? '' : token.slice(hash);
|
|
340
|
+
const itemFile = basename(context.itemsPath);
|
|
341
|
+
if (path === itemFile || path.endsWith(`/${itemFile}`)) {
|
|
342
|
+
const replacement = rewriteItemToken(path, context);
|
|
343
|
+
return replacement === null || replacement === path
|
|
344
|
+
? span
|
|
345
|
+
: `${run}${leading}${replacement}${anchor}${trailing}${run}`;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
warnCodeOccurrences(inner, context);
|
|
349
|
+
return span;
|
|
350
|
+
}
|
|
351
|
+
/** Rewrite every token in `content` that ends in the item's file name. */
|
|
352
|
+
function rewriteItemTokens(content, context) {
|
|
353
|
+
const itemFile = basename(context.itemsPath);
|
|
354
|
+
let output = '';
|
|
355
|
+
let cursor = 0;
|
|
356
|
+
let index = content.indexOf(itemFile);
|
|
357
|
+
while (index !== -1) {
|
|
358
|
+
const end = index + itemFile.length;
|
|
359
|
+
const before = index === 0 ? '' : content[index - 1];
|
|
360
|
+
const after = content[end] ?? '';
|
|
361
|
+
const boundedBefore = before === '' || before === '/' || TOKEN_DELIMITERS.has(before);
|
|
362
|
+
const boundedAfter = after === '' || !/[A-Za-z0-9_-]/u.test(after);
|
|
363
|
+
if (boundedBefore && boundedAfter) {
|
|
364
|
+
let start = index;
|
|
365
|
+
while (start > cursor && !TOKEN_DELIMITERS.has(content[start - 1])) {
|
|
366
|
+
start -= 1;
|
|
367
|
+
}
|
|
368
|
+
const token = content.slice(start, end);
|
|
369
|
+
const replacement = rewriteItemToken(token, context);
|
|
370
|
+
if (replacement !== null && replacement !== token) {
|
|
371
|
+
output += content.slice(cursor, start) + replacement;
|
|
372
|
+
cursor = end;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
index = content.indexOf(itemFile, end);
|
|
376
|
+
}
|
|
377
|
+
return output + content.slice(cursor);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Rebase one relative link target of the moved item, or return it as-is.
|
|
381
|
+
* Only a target that existed relative to `items/` is rebased, so prose or an
|
|
382
|
+
* already-broken target is never rewritten into a new broken path.
|
|
383
|
+
*/
|
|
384
|
+
function rebaseTarget(target, context) {
|
|
385
|
+
if (!isRelativeTarget(target)) {
|
|
386
|
+
return target;
|
|
387
|
+
}
|
|
388
|
+
const absolute = resolve(context.baseDirectory, target);
|
|
389
|
+
if (absolute === context.itemsPath ||
|
|
390
|
+
resolve(context.currentDirectory, target) === absolute ||
|
|
391
|
+
!pathExistsSync(absolute)) {
|
|
392
|
+
return target;
|
|
393
|
+
}
|
|
394
|
+
return relativeFrom(context.currentDirectory, absolute, target);
|
|
395
|
+
}
|
|
396
|
+
/** Rebase `path?query#anchor`, keeping the query and anchor as written. */
|
|
397
|
+
function rebaseWithAnchor(target, context) {
|
|
398
|
+
const split = target.search(/[?#]/u);
|
|
399
|
+
const path = split === -1 ? target : target.slice(0, split);
|
|
400
|
+
const suffix = split === -1 ? '' : target.slice(split);
|
|
401
|
+
return path.length === 0 ? target : `${rebaseTarget(path, context)}${suffix}`;
|
|
402
|
+
}
|
|
403
|
+
/** A CommonMark link title: `"…"`, `'…'`, or `(…)`. */
|
|
404
|
+
const LINK_TITLE = String.raw `(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\))`;
|
|
405
|
+
const INLINE_LINK = new RegExp(String.raw `\]\((?:<([^<>\n]*)>|([^)\s<]+))((?:\s+${LINK_TITLE})?)\)`, 'gu');
|
|
406
|
+
const REFERENCE_DEFINITION = new RegExp(String.raw `^( {0,3}\[(?!\^)[^\]\n]+\]:[ \t]*)(?:<([^<>\n]*)>|([^\s<>]+))((?:[ \t]+${LINK_TITLE})?[ \t]*\r?)$`, 'gmu');
|
|
407
|
+
/**
|
|
408
|
+
* Rebase the moved item's inline `[text](target "title")` links (angle
|
|
409
|
+
* destinations and every title form) from `items/` to `archived/`.
|
|
410
|
+
*/
|
|
411
|
+
function rebaseMovedItemInlineLinks(content, context) {
|
|
412
|
+
return content.replace(INLINE_LINK, (_match, angled, bare, title) => angled !== undefined
|
|
413
|
+
? `](<${rebaseWithAnchor(angled, context)}>${title})`
|
|
414
|
+
: `](${rebaseWithAnchor(bare, context)}${title})`);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Rebase the moved item's reference definitions (`[label]: <dest> "title"`)
|
|
418
|
+
* outside fenced code. Footnotes (`[^n]:`) and `[label]: prose` lines — a
|
|
419
|
+
* destination followed by anything but a title — are not definitions and are
|
|
420
|
+
* left untouched.
|
|
421
|
+
*/
|
|
422
|
+
function rebaseMovedItemDefinitions(content, context) {
|
|
423
|
+
const segments = splitMarkdownCode(content);
|
|
424
|
+
return segments
|
|
425
|
+
.map((segment, index) => {
|
|
426
|
+
// Code spans and fences stay exactly as written.
|
|
427
|
+
if (segment.kind !== 'prose') {
|
|
428
|
+
return segment.text;
|
|
429
|
+
}
|
|
430
|
+
// A prose segment that follows a code span mid-line does not start a
|
|
431
|
+
// line, so a match at its offset 0 is not a definition.
|
|
432
|
+
const previous = index === 0 ? null : segments[index - 1].text;
|
|
433
|
+
const startsLine = previous === null || previous.endsWith('\n');
|
|
434
|
+
return segment.text.replace(REFERENCE_DEFINITION, (match, prefix, angled, bare, rest, offset) => {
|
|
435
|
+
if (offset === 0 && !startsLine) {
|
|
436
|
+
return match;
|
|
437
|
+
}
|
|
438
|
+
return angled !== undefined
|
|
439
|
+
? `${prefix}<${rebaseWithAnchor(angled, context)}>${rest}`
|
|
440
|
+
: `${prefix}${rebaseWithAnchor(bare, context)}${rest}`;
|
|
441
|
+
});
|
|
442
|
+
})
|
|
443
|
+
.join('');
|
|
444
|
+
}
|
|
445
|
+
/** Rewrite one file's content: prose tokens, citation spans, moved links. */
|
|
446
|
+
function rewriteContent(content, context, moved) {
|
|
447
|
+
const itemFile = basename(context.itemsPath);
|
|
448
|
+
let next = content;
|
|
449
|
+
if (content.includes(itemFile)) {
|
|
450
|
+
next = splitMarkdownCode(content)
|
|
451
|
+
.map((segment) => {
|
|
452
|
+
if (segment.kind === 'prose') {
|
|
453
|
+
return rewriteItemTokens(segment.text, context);
|
|
454
|
+
}
|
|
455
|
+
if (segment.kind === 'span') {
|
|
456
|
+
return rewriteCodeSpan(segment.text, context);
|
|
457
|
+
}
|
|
458
|
+
warnCodeOccurrences(segment.text, context);
|
|
459
|
+
return segment.text;
|
|
460
|
+
})
|
|
461
|
+
.join('');
|
|
462
|
+
}
|
|
463
|
+
if (moved) {
|
|
464
|
+
next = splitMarkdownCode(next)
|
|
465
|
+
.map((segment) => segment.kind === 'prose'
|
|
466
|
+
? rebaseMovedItemInlineLinks(segment.text, context)
|
|
467
|
+
: segment.text)
|
|
468
|
+
.join('');
|
|
469
|
+
next = rebaseMovedItemDefinitions(next, context);
|
|
470
|
+
}
|
|
471
|
+
return next;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* After `items/<id>.md` moved to `archived/<id>.md`, rewrite every reference
|
|
475
|
+
* to the old path in Markdown under `.oat/repo/**` (links and repository-root
|
|
476
|
+
* path strings such as `oat_external_plan_sources` frontmatter). An inline code
|
|
477
|
+
* span is rewritten only when its whole content is a path to the item (the
|
|
478
|
+
* external-plan citation form); other spans and fenced code are left as
|
|
479
|
+
* written with a warning, so a recorded command keeps its meaning. References that name the item's `items/` path
|
|
480
|
+
* but cannot be resolved are reported as warnings and left untouched. The
|
|
481
|
+
* rewrite is idempotent, so a re-run on an already-archived item is safe.
|
|
482
|
+
*/
|
|
483
|
+
export async function rewriteInboundReferences(backlogRoot, itemsPath, archivedPath, options = {}) {
|
|
484
|
+
const rebaseMovedItem = options.rebaseMovedItem ?? true;
|
|
485
|
+
const layout = resolveLayout(backlogRoot);
|
|
486
|
+
const absoluteItemsPath = resolve(itemsPath);
|
|
487
|
+
const absoluteArchivedPath = resolve(archivedPath);
|
|
488
|
+
const displayRoot = layout.repoRoot ?? layout.scanRoot;
|
|
489
|
+
const rewritten = [];
|
|
490
|
+
const warnings = [];
|
|
491
|
+
// The moved item goes first, so a failure on a later file never leaves its
|
|
492
|
+
// own links half-rebased: a retry (which skips the rebase) stays correct.
|
|
493
|
+
const files = (await listMarkdownFiles(layout.scanRoot)).sort((left, right) => Number(resolve(right) === absoluteArchivedPath) -
|
|
494
|
+
Number(resolve(left) === absoluteArchivedPath) ||
|
|
495
|
+
left.localeCompare(right));
|
|
496
|
+
for (const file of files) {
|
|
497
|
+
const absoluteFile = resolve(file);
|
|
498
|
+
const moved = rebaseMovedItem && absoluteFile === absoluteArchivedPath;
|
|
499
|
+
const { content, identity } = await readNoFollow(absoluteFile);
|
|
500
|
+
const context = {
|
|
501
|
+
layout,
|
|
502
|
+
itemsPath: absoluteItemsPath,
|
|
503
|
+
archivedPath: absoluteArchivedPath,
|
|
504
|
+
fileName: toPosix(relative(displayRoot, absoluteFile)),
|
|
505
|
+
baseDirectory: moved ? dirname(absoluteItemsPath) : dirname(absoluteFile),
|
|
506
|
+
currentDirectory: dirname(absoluteFile),
|
|
507
|
+
warnings,
|
|
508
|
+
};
|
|
509
|
+
const next = rewriteContent(content, context, moved);
|
|
510
|
+
if (next !== content) {
|
|
511
|
+
await replaceAtomically(absoluteFile, next, identity);
|
|
512
|
+
rewritten.push(context.fileName);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return { rewritten, warnings };
|
|
516
|
+
}
|
|
@@ -12,6 +12,7 @@ interface ConfigCommandDependencies {
|
|
|
12
12
|
readOatConfigForDefaultScopeRepair: (repoRoot: string) => Promise<OatConfig>;
|
|
13
13
|
readOatConfigForDocumentationExcludesRepair: (repoRoot: string) => Promise<OatConfig>;
|
|
14
14
|
readOatConfigForInstructionPointerExcludesRepair: (repoRoot: string) => Promise<OatConfig>;
|
|
15
|
+
readOatConfigForInstructionSyncStrategyRepair: (repoRoot: string) => Promise<OatConfig>;
|
|
15
16
|
readOatConfigWithWarnings: (repoRoot: string) => Promise<OatConfigRead>;
|
|
16
17
|
writeOatConfig: (repoRoot: string, config: OatConfig) => Promise<void>;
|
|
17
18
|
readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAIhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAoBzC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"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"}
|