agentopology 0.1.0
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/LICENSE +191 -0
- package/README.md +450 -0
- package/dist/analyzer/__tests__/analyzer.test.d.ts +2 -0
- package/dist/analyzer/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/analyzer/__tests__/analyzer.test.js +258 -0
- package/dist/analyzer/__tests__/analyzer.test.js.map +1 -0
- package/dist/analyzer/index.d.ts +44 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +308 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/bindings/__tests__/bindings.test.d.ts +2 -0
- package/dist/bindings/__tests__/bindings.test.d.ts.map +1 -0
- package/dist/bindings/__tests__/bindings.test.js +2089 -0
- package/dist/bindings/__tests__/bindings.test.js.map +1 -0
- package/dist/bindings/claude-code.d.ts +12 -0
- package/dist/bindings/claude-code.d.ts.map +1 -0
- package/dist/bindings/claude-code.js +2063 -0
- package/dist/bindings/claude-code.js.map +1 -0
- package/dist/bindings/codex.d.ts +18 -0
- package/dist/bindings/codex.d.ts.map +1 -0
- package/dist/bindings/codex.js +1633 -0
- package/dist/bindings/codex.js.map +1 -0
- package/dist/bindings/copilot-cli.d.ts +20 -0
- package/dist/bindings/copilot-cli.d.ts.map +1 -0
- package/dist/bindings/copilot-cli.js +1515 -0
- package/dist/bindings/copilot-cli.js.map +1 -0
- package/dist/bindings/cursor.d.ts +17 -0
- package/dist/bindings/cursor.d.ts.map +1 -0
- package/dist/bindings/cursor.js +1715 -0
- package/dist/bindings/cursor.js.map +1 -0
- package/dist/bindings/gemini-cli.d.ts +18 -0
- package/dist/bindings/gemini-cli.d.ts.map +1 -0
- package/dist/bindings/gemini-cli.js +1327 -0
- package/dist/bindings/gemini-cli.js.map +1 -0
- package/dist/bindings/index.d.ts +20 -0
- package/dist/bindings/index.d.ts.map +1 -0
- package/dist/bindings/index.js +33 -0
- package/dist/bindings/index.js.map +1 -0
- package/dist/bindings/kiro.d.ts +17 -0
- package/dist/bindings/kiro.d.ts.map +1 -0
- package/dist/bindings/kiro.js +1624 -0
- package/dist/bindings/kiro.js.map +1 -0
- package/dist/bindings/openclaw.d.ts +18 -0
- package/dist/bindings/openclaw.d.ts.map +1 -0
- package/dist/bindings/openclaw.js +1578 -0
- package/dist/bindings/openclaw.js.map +1 -0
- package/dist/bindings/types.d.ts +33 -0
- package/dist/bindings/types.d.ts.map +1 -0
- package/dist/bindings/types.js +23 -0
- package/dist/bindings/types.js.map +1 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +679 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/docs/__tests__/docs.test.d.ts +2 -0
- package/dist/docs/__tests__/docs.test.d.ts.map +1 -0
- package/dist/docs/__tests__/docs.test.js +438 -0
- package/dist/docs/__tests__/docs.test.js.map +1 -0
- package/dist/docs/content.d.ts +16 -0
- package/dist/docs/content.d.ts.map +1 -0
- package/dist/docs/content.js +3273 -0
- package/dist/docs/content.js.map +1 -0
- package/dist/docs/index.d.ts +17 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +117 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/exporters/__tests__/exporters.test.d.ts +2 -0
- package/dist/exporters/__tests__/exporters.test.d.ts.map +1 -0
- package/dist/exporters/__tests__/exporters.test.js +731 -0
- package/dist/exporters/__tests__/exporters.test.js.map +1 -0
- package/dist/exporters/index.d.ts +13 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +18 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/json.d.ts +8 -0
- package/dist/exporters/json.d.ts.map +1 -0
- package/dist/exporters/json.js +25 -0
- package/dist/exporters/json.js.map +1 -0
- package/dist/exporters/markdown.d.ts +9 -0
- package/dist/exporters/markdown.d.ts.map +1 -0
- package/dist/exporters/markdown.js +686 -0
- package/dist/exporters/markdown.js.map +1 -0
- package/dist/exporters/mermaid.d.ts +11 -0
- package/dist/exporters/mermaid.d.ts.map +1 -0
- package/dist/exporters/mermaid.js +161 -0
- package/dist/exporters/mermaid.js.map +1 -0
- package/dist/exporters/types.d.ts +22 -0
- package/dist/exporters/types.d.ts.map +1 -0
- package/dist/exporters/types.js +10 -0
- package/dist/exporters/types.js.map +1 -0
- package/dist/import/__tests__/import.test.d.ts +2 -0
- package/dist/import/__tests__/import.test.d.ts.map +1 -0
- package/dist/import/__tests__/import.test.js +781 -0
- package/dist/import/__tests__/import.test.js.map +1 -0
- package/dist/import/claude-code.d.ts +38 -0
- package/dist/import/claude-code.d.ts.map +1 -0
- package/dist/import/claude-code.js +897 -0
- package/dist/import/claude-code.js.map +1 -0
- package/dist/import/index.d.ts +18 -0
- package/dist/import/index.d.ts.map +1 -0
- package/dist/import/index.js +28 -0
- package/dist/import/index.js.map +1 -0
- package/dist/import/serializer.d.ts +11 -0
- package/dist/import/serializer.d.ts.map +1 -0
- package/dist/import/serializer.js +960 -0
- package/dist/import/serializer.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/__tests__/parser.test.d.ts +2 -0
- package/dist/parser/__tests__/parser.test.d.ts.map +1 -0
- package/dist/parser/__tests__/parser.test.js +6514 -0
- package/dist/parser/__tests__/parser.test.js.map +1 -0
- package/dist/parser/ast.d.ts +708 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/ast.js +12 -0
- package/dist/parser/ast.js.map +1 -0
- package/dist/parser/index.d.ts +253 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +1985 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/lexer.d.ts +100 -0
- package/dist/parser/lexer.d.ts.map +1 -0
- package/dist/parser/lexer.js +304 -0
- package/dist/parser/lexer.js.map +1 -0
- package/dist/parser/validator.d.ts +42 -0
- package/dist/parser/validator.d.ts.map +1 -0
- package/dist/parser/validator.js +2568 -0
- package/dist/parser/validator.js.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.d.ts +7 -0
- package/dist/scaffold/__tests__/scaffold.test.d.ts.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.js +295 -0
- package/dist/scaffold/__tests__/scaffold.test.js.map +1 -0
- package/dist/scaffold/incremental.d.ts +43 -0
- package/dist/scaffold/incremental.d.ts.map +1 -0
- package/dist/scaffold/incremental.js +185 -0
- package/dist/scaffold/incremental.js.map +1 -0
- package/dist/scaffold/index.d.ts +13 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +13 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/manifest.d.ts +30 -0
- package/dist/scaffold/manifest.d.ts.map +1 -0
- package/dist/scaffold/manifest.js +71 -0
- package/dist/scaffold/manifest.js.map +1 -0
- package/dist/scaffold/merge.d.ts +52 -0
- package/dist/scaffold/merge.d.ts.map +1 -0
- package/dist/scaffold/merge.js +134 -0
- package/dist/scaffold/merge.js.map +1 -0
- package/dist/scaffold/types.d.ts +51 -0
- package/dist/scaffold/types.d.ts.map +1 -0
- package/dist/scaffold/types.js +10 -0
- package/dist/scaffold/types.js.map +1 -0
- package/dist/sync/index.d.ts +34 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +392 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/visualizer/index.d.ts +17 -0
- package/dist/visualizer/index.d.ts.map +1 -0
- package/dist/visualizer/index.js +1976 -0
- package/dist/visualizer/index.js.map +1 -0
- package/examples/code-review.at +167 -0
- package/examples/data-processing.at +296 -0
- package/examples/openclaw-assistant.at +142 -0
- package/examples/scheduled-monitor.at +193 -0
- package/examples/simple-pipeline.at +163 -0
- package/package.json +77 -0
- package/skill/SKILL.md +403 -0
- package/skill/scripts/emit.ts +853 -0
- package/skill/scripts/visualize.ts +89 -0
- package/spec/grammar.md +1466 -0
- package/spec/reserved-keywords.md +147 -0
- package/spec/validation.md +535 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental scaffold orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Computes a plan of file actions (create, update, delete, unchanged, conflict)
|
|
5
|
+
* by comparing freshly generated files against what is on disk and what was
|
|
6
|
+
* previously tracked in the scaffold manifest.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { mergeAgentFile, shouldOverwriteScript, deepMergeJson } from "./merge.js";
|
|
13
|
+
/**
|
|
14
|
+
* Compute an incremental scaffold plan.
|
|
15
|
+
*
|
|
16
|
+
* Compares generated files against what exists on disk and the previous
|
|
17
|
+
* manifest to determine the minimal set of file operations needed.
|
|
18
|
+
*
|
|
19
|
+
* @param basePath - Absolute path to the project root
|
|
20
|
+
* @param target - Binding target name (e.g. "claude-code")
|
|
21
|
+
* @param generatedFiles - Files produced by the binding's scaffold function
|
|
22
|
+
* @param manifest - Previous scaffold manifest (null if first scaffold)
|
|
23
|
+
* @returns Array of file actions to execute
|
|
24
|
+
*/
|
|
25
|
+
export function computeIncrementalPlan(basePath, target, generatedFiles, manifest) {
|
|
26
|
+
const actions = [];
|
|
27
|
+
// Build map of generated files by path
|
|
28
|
+
const generatedMap = new Map();
|
|
29
|
+
for (const file of generatedFiles) {
|
|
30
|
+
generatedMap.set(file.path, file);
|
|
31
|
+
}
|
|
32
|
+
// Process each generated file
|
|
33
|
+
for (const file of generatedFiles) {
|
|
34
|
+
const absPath = join(basePath, file.path);
|
|
35
|
+
const category = file.category ?? "machine";
|
|
36
|
+
const onDisk = existsSync(absPath);
|
|
37
|
+
if (!onDisk) {
|
|
38
|
+
actions.push({ type: "create", path: file.path, content: file.content });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const existingContent = readFileSync(absPath, "utf-8");
|
|
42
|
+
const manifestEntry = manifest?.files[file.path] ?? null;
|
|
43
|
+
switch (category) {
|
|
44
|
+
case "agent": {
|
|
45
|
+
const merged = mergeAgentFile(existingContent, file.content, target);
|
|
46
|
+
if (merged === existingContent) {
|
|
47
|
+
actions.push({ type: "unchanged", path: file.path });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
actions.push({
|
|
51
|
+
type: "update",
|
|
52
|
+
path: file.path,
|
|
53
|
+
content: merged,
|
|
54
|
+
detail: "prompt preserved",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "script": {
|
|
60
|
+
const manifestHash = manifestEntry?.hash ?? "";
|
|
61
|
+
if (shouldOverwriteScript(existingContent, manifestHash)) {
|
|
62
|
+
// User hasn't edited — safe to overwrite
|
|
63
|
+
if (file.content === existingContent) {
|
|
64
|
+
actions.push({ type: "unchanged", path: file.path });
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
actions.push({
|
|
68
|
+
type: "update",
|
|
69
|
+
path: file.path,
|
|
70
|
+
content: file.content,
|
|
71
|
+
detail: "script regenerated",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// User has edited — conflict
|
|
77
|
+
actions.push({
|
|
78
|
+
type: "conflict",
|
|
79
|
+
path: file.path,
|
|
80
|
+
content: file.content,
|
|
81
|
+
detail: "user edited",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case "shared-config": {
|
|
87
|
+
const merged = deepMergeJson(existingContent, file.content);
|
|
88
|
+
if (merged === existingContent) {
|
|
89
|
+
actions.push({ type: "unchanged", path: file.path });
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
actions.push({
|
|
93
|
+
type: "update",
|
|
94
|
+
path: file.path,
|
|
95
|
+
content: merged,
|
|
96
|
+
detail: "config merged",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "machine":
|
|
102
|
+
default: {
|
|
103
|
+
if (file.content === existingContent) {
|
|
104
|
+
actions.push({ type: "unchanged", path: file.path });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
actions.push({
|
|
108
|
+
type: "update",
|
|
109
|
+
path: file.path,
|
|
110
|
+
content: file.content,
|
|
111
|
+
detail: "config updated",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Check for files in manifest that are no longer generated (deletions)
|
|
119
|
+
if (manifest) {
|
|
120
|
+
for (const manifestPath of Object.keys(manifest.files)) {
|
|
121
|
+
if (!generatedMap.has(manifestPath)) {
|
|
122
|
+
actions.push({ type: "delete", path: manifestPath });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return actions;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Execute a set of file actions against the filesystem.
|
|
130
|
+
*
|
|
131
|
+
* @param basePath - Absolute path to the project root
|
|
132
|
+
* @param actions - File actions computed by {@link computeIncrementalPlan}
|
|
133
|
+
* @param options - Execution options
|
|
134
|
+
* @returns Counts of each action type performed
|
|
135
|
+
*/
|
|
136
|
+
export function executeActions(basePath, actions, options) {
|
|
137
|
+
const counts = { created: 0, updated: 0, deleted: 0, unchanged: 0, conflicts: 0 };
|
|
138
|
+
for (const action of actions) {
|
|
139
|
+
const absPath = join(basePath, action.path);
|
|
140
|
+
switch (action.type) {
|
|
141
|
+
case "create": {
|
|
142
|
+
const dir = dirname(absPath);
|
|
143
|
+
if (!existsSync(dir)) {
|
|
144
|
+
mkdirSync(dir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
writeFileSync(absPath, action.content, "utf-8");
|
|
147
|
+
counts.created++;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "update": {
|
|
151
|
+
const dir = dirname(absPath);
|
|
152
|
+
if (!existsSync(dir)) {
|
|
153
|
+
mkdirSync(dir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
writeFileSync(absPath, action.content, "utf-8");
|
|
156
|
+
counts.updated++;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case "delete": {
|
|
160
|
+
if (options.prune && existsSync(absPath)) {
|
|
161
|
+
unlinkSync(absPath);
|
|
162
|
+
}
|
|
163
|
+
counts.deleted++;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case "conflict": {
|
|
167
|
+
if (options.force) {
|
|
168
|
+
const dir = dirname(absPath);
|
|
169
|
+
if (!existsSync(dir)) {
|
|
170
|
+
mkdirSync(dir, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
writeFileSync(absPath, action.content, "utf-8");
|
|
173
|
+
}
|
|
174
|
+
counts.conflicts++;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "unchanged": {
|
|
178
|
+
counts.unchanged++;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return counts;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=incremental.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incremental.js","sourceRoot":"","sources":["../../src/scaffold/incremental.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* Provides manifest tracking and merge logic so that `scaffold` can
|
|
5
|
+
* intelligently update files on disk without destroying user edits.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export * from "./types.js";
|
|
10
|
+
export * from "./manifest.js";
|
|
11
|
+
export * from "./merge.js";
|
|
12
|
+
export * from "./incremental.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* Provides manifest tracking and merge logic so that `scaffold` can
|
|
5
|
+
* intelligently update files on disk without destroying user edits.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export * from "./types.js";
|
|
10
|
+
export * from "./manifest.js";
|
|
11
|
+
export * from "./merge.js";
|
|
12
|
+
export * from "./incremental.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scaffold/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest I/O for the incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes `.scaffold-manifest.json` files that track what was
|
|
5
|
+
* generated, enabling intelligent re-scaffolding.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { ScaffoldManifest } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Return the platform-specific manifest path (relative to project root).
|
|
12
|
+
*
|
|
13
|
+
* Each binding stores the manifest inside its own config directory so it
|
|
14
|
+
* doesn't collide with other bindings scaffolded into the same project.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getManifestPath(target: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Read an existing scaffold manifest from disk.
|
|
19
|
+
* Returns `null` if the manifest does not exist or cannot be parsed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readManifest(basePath: string, target: string): ScaffoldManifest | null;
|
|
22
|
+
/**
|
|
23
|
+
* Write a scaffold manifest to disk, creating parent directories as needed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeManifest(basePath: string, target: string, manifest: ScaffoldManifest): void;
|
|
26
|
+
/**
|
|
27
|
+
* Compute a SHA-256 hex digest of a string.
|
|
28
|
+
*/
|
|
29
|
+
export declare function hashContent(content: string): string;
|
|
30
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/scaffold/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBtD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,IAAI,CAUzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,GACzB,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest I/O for the incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes `.scaffold-manifest.json` files that track what was
|
|
5
|
+
* generated, enabling intelligent re-scaffolding.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
const MANIFEST_FILENAME = ".scaffold-manifest.json";
|
|
13
|
+
/**
|
|
14
|
+
* Return the platform-specific manifest path (relative to project root).
|
|
15
|
+
*
|
|
16
|
+
* Each binding stores the manifest inside its own config directory so it
|
|
17
|
+
* doesn't collide with other bindings scaffolded into the same project.
|
|
18
|
+
*/
|
|
19
|
+
export function getManifestPath(target) {
|
|
20
|
+
switch (target) {
|
|
21
|
+
case "claude-code":
|
|
22
|
+
return join(".claude", MANIFEST_FILENAME);
|
|
23
|
+
case "codex":
|
|
24
|
+
return join(".codex", MANIFEST_FILENAME);
|
|
25
|
+
case "gemini-cli":
|
|
26
|
+
return join(".gemini", MANIFEST_FILENAME);
|
|
27
|
+
case "copilot-cli":
|
|
28
|
+
return join(".github", MANIFEST_FILENAME);
|
|
29
|
+
case "openclaw":
|
|
30
|
+
return join(".openclaw", MANIFEST_FILENAME);
|
|
31
|
+
case "kiro":
|
|
32
|
+
return join(".kiro", MANIFEST_FILENAME);
|
|
33
|
+
default:
|
|
34
|
+
// SDK bindings and unknown targets use root
|
|
35
|
+
return MANIFEST_FILENAME;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read an existing scaffold manifest from disk.
|
|
40
|
+
* Returns `null` if the manifest does not exist or cannot be parsed.
|
|
41
|
+
*/
|
|
42
|
+
export function readManifest(basePath, target) {
|
|
43
|
+
const manifestPath = join(basePath, getManifestPath(target));
|
|
44
|
+
if (!existsSync(manifestPath))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const raw = readFileSync(manifestPath, "utf-8");
|
|
48
|
+
return JSON.parse(raw);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Write a scaffold manifest to disk, creating parent directories as needed.
|
|
56
|
+
*/
|
|
57
|
+
export function writeManifest(basePath, target, manifest) {
|
|
58
|
+
const manifestPath = join(basePath, getManifestPath(target));
|
|
59
|
+
const dir = dirname(manifestPath);
|
|
60
|
+
if (!existsSync(dir)) {
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Compute a SHA-256 hex digest of a string.
|
|
67
|
+
*/
|
|
68
|
+
export function hashContent(content) {
|
|
69
|
+
return createHash("sha256").update(content, "utf-8").digest("hex");
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/scaffold/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAI1C,MAAM,iBAAiB,GAAG,yBAAyB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC5C,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC3C,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC5C,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC5C,KAAK,UAAU;YACb,OAAO,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAC9C,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAC1C;YACE,4CAA4C;YAC5C,OAAO,iBAAiB,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAgB,EAChB,MAAc;IAEd,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,MAAc,EACd,QAA0B;IAE1B,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACjF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge logic for incremental scaffold.
|
|
3
|
+
*
|
|
4
|
+
* Handles preserving user-edited content (HUMAN zone) in agent files
|
|
5
|
+
* while updating machine-generated sections, and hash-based overwrite
|
|
6
|
+
* decisions for script files.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Merge an existing agent file with a freshly generated one.
|
|
12
|
+
*
|
|
13
|
+
* Preserves the HUMAN zone (everything under `## Instructions` until the next
|
|
14
|
+
* structural heading) from the existing file, while taking all machine-generated
|
|
15
|
+
* sections from the generated file.
|
|
16
|
+
*
|
|
17
|
+
* If the existing file has no `## Instructions` section, returns the generated
|
|
18
|
+
* file as-is.
|
|
19
|
+
*
|
|
20
|
+
* @param existing - Current file content on disk
|
|
21
|
+
* @param generated - Freshly generated file content
|
|
22
|
+
* @param binding - Target binding name (e.g. "claude-code")
|
|
23
|
+
* @returns Merged file content
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeAgentFile(existing: string, generated: string, binding: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Determine whether a script file should be overwritten.
|
|
28
|
+
*
|
|
29
|
+
* Compares the hash of the existing file content against the hash stored
|
|
30
|
+
* in the manifest. If they match, the user has not edited the file and
|
|
31
|
+
* it is safe to overwrite.
|
|
32
|
+
*
|
|
33
|
+
* @param existingContent - Current file content on disk
|
|
34
|
+
* @param manifestHash - Hash from the scaffold manifest
|
|
35
|
+
* @returns `true` if the file is unmodified and safe to overwrite
|
|
36
|
+
*/
|
|
37
|
+
export declare function shouldOverwriteScript(existingContent: string, manifestHash: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Deep-merge two JSON config files.
|
|
40
|
+
*
|
|
41
|
+
* Scaffold-generated keys are added/updated into the existing config,
|
|
42
|
+
* but user-only keys (not present in generated) are preserved.
|
|
43
|
+
*
|
|
44
|
+
* For nested objects (like mcpServers), merges by key — scaffold stubs
|
|
45
|
+
* don't overwrite existing entries that have real config.
|
|
46
|
+
*
|
|
47
|
+
* @param existingRaw - Current file content on disk (JSON string)
|
|
48
|
+
* @param generatedRaw - Freshly generated file content (JSON string)
|
|
49
|
+
* @returns Merged JSON string, or existingRaw if parse fails
|
|
50
|
+
*/
|
|
51
|
+
export declare function deepMergeJson(existingRaw: string, generatedRaw: string): string;
|
|
52
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/scaffold/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuBH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CA4CR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAET;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAc/E"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge logic for incremental scaffold.
|
|
3
|
+
*
|
|
4
|
+
* Handles preserving user-edited content (HUMAN zone) in agent files
|
|
5
|
+
* while updating machine-generated sections, and hash-based overwrite
|
|
6
|
+
* decisions for script files.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { extractSectionUntilKnownHeading, CLAUDE_CODE_STRUCTURAL_HEADINGS, COPILOT_STRUCTURAL_HEADINGS, } from "../sync/index.js";
|
|
11
|
+
import { hashContent } from "./manifest.js";
|
|
12
|
+
/**
|
|
13
|
+
* Return the list of known structural headings for a binding.
|
|
14
|
+
* Falls back to the Claude Code list for unknown bindings.
|
|
15
|
+
*/
|
|
16
|
+
function getStructuralHeadings(binding) {
|
|
17
|
+
switch (binding) {
|
|
18
|
+
case "copilot-cli":
|
|
19
|
+
return COPILOT_STRUCTURAL_HEADINGS;
|
|
20
|
+
case "claude-code":
|
|
21
|
+
default:
|
|
22
|
+
return CLAUDE_CODE_STRUCTURAL_HEADINGS;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Merge an existing agent file with a freshly generated one.
|
|
27
|
+
*
|
|
28
|
+
* Preserves the HUMAN zone (everything under `## Instructions` until the next
|
|
29
|
+
* structural heading) from the existing file, while taking all machine-generated
|
|
30
|
+
* sections from the generated file.
|
|
31
|
+
*
|
|
32
|
+
* If the existing file has no `## Instructions` section, returns the generated
|
|
33
|
+
* file as-is.
|
|
34
|
+
*
|
|
35
|
+
* @param existing - Current file content on disk
|
|
36
|
+
* @param generated - Freshly generated file content
|
|
37
|
+
* @param binding - Target binding name (e.g. "claude-code")
|
|
38
|
+
* @returns Merged file content
|
|
39
|
+
*/
|
|
40
|
+
export function mergeAgentFile(existing, generated, binding) {
|
|
41
|
+
const structuralHeadings = getStructuralHeadings(binding);
|
|
42
|
+
// Extract the HUMAN zone from the existing file
|
|
43
|
+
const humanContent = extractSectionUntilKnownHeading(existing, "## Instructions", structuralHeadings);
|
|
44
|
+
// If no Instructions section exists in existing, return generated as-is
|
|
45
|
+
if (humanContent === null) {
|
|
46
|
+
return generated;
|
|
47
|
+
}
|
|
48
|
+
// Find the ## Instructions section in the generated file
|
|
49
|
+
const instructionsPattern = /^## Instructions\s*$/m;
|
|
50
|
+
const instrMatch = instructionsPattern.exec(generated);
|
|
51
|
+
if (!instrMatch) {
|
|
52
|
+
// Generated file has no Instructions section — return it as-is
|
|
53
|
+
return generated;
|
|
54
|
+
}
|
|
55
|
+
// Find where the generated instructions content ends (next structural heading or EOF)
|
|
56
|
+
const afterHeading = instrMatch.index + instrMatch[0].length;
|
|
57
|
+
const rest = generated.slice(afterHeading);
|
|
58
|
+
let endPos = rest.length;
|
|
59
|
+
for (const kh of structuralHeadings) {
|
|
60
|
+
const khPattern = new RegExp(`^${kh.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
61
|
+
const khMatch = khPattern.exec(rest);
|
|
62
|
+
if (khMatch && khMatch.index < endPos) {
|
|
63
|
+
endPos = khMatch.index;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Build merged file: everything before instructions content + human content + everything after
|
|
67
|
+
const beforeContent = generated.slice(0, afterHeading);
|
|
68
|
+
const afterContent = rest.slice(endPos);
|
|
69
|
+
return beforeContent + "\n\n" + humanContent + "\n\n" + afterContent;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Determine whether a script file should be overwritten.
|
|
73
|
+
*
|
|
74
|
+
* Compares the hash of the existing file content against the hash stored
|
|
75
|
+
* in the manifest. If they match, the user has not edited the file and
|
|
76
|
+
* it is safe to overwrite.
|
|
77
|
+
*
|
|
78
|
+
* @param existingContent - Current file content on disk
|
|
79
|
+
* @param manifestHash - Hash from the scaffold manifest
|
|
80
|
+
* @returns `true` if the file is unmodified and safe to overwrite
|
|
81
|
+
*/
|
|
82
|
+
export function shouldOverwriteScript(existingContent, manifestHash) {
|
|
83
|
+
return hashContent(existingContent) === manifestHash;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Deep-merge two JSON config files.
|
|
87
|
+
*
|
|
88
|
+
* Scaffold-generated keys are added/updated into the existing config,
|
|
89
|
+
* but user-only keys (not present in generated) are preserved.
|
|
90
|
+
*
|
|
91
|
+
* For nested objects (like mcpServers), merges by key — scaffold stubs
|
|
92
|
+
* don't overwrite existing entries that have real config.
|
|
93
|
+
*
|
|
94
|
+
* @param existingRaw - Current file content on disk (JSON string)
|
|
95
|
+
* @param generatedRaw - Freshly generated file content (JSON string)
|
|
96
|
+
* @returns Merged JSON string, or existingRaw if parse fails
|
|
97
|
+
*/
|
|
98
|
+
export function deepMergeJson(existingRaw, generatedRaw) {
|
|
99
|
+
let existing;
|
|
100
|
+
let generated;
|
|
101
|
+
try {
|
|
102
|
+
existing = JSON.parse(existingRaw);
|
|
103
|
+
generated = JSON.parse(generatedRaw);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// If either file isn't valid JSON, return existing unchanged
|
|
107
|
+
return existingRaw;
|
|
108
|
+
}
|
|
109
|
+
const merged = mergeObjects(existing, generated);
|
|
110
|
+
return JSON.stringify(merged, null, 2) + "\n";
|
|
111
|
+
}
|
|
112
|
+
function mergeObjects(existing, generated) {
|
|
113
|
+
// Start with a copy of existing (preserves user-only keys)
|
|
114
|
+
const result = { ...existing };
|
|
115
|
+
for (const [key, genValue] of Object.entries(generated)) {
|
|
116
|
+
const exValue = existing[key];
|
|
117
|
+
if (genValue !== null &&
|
|
118
|
+
typeof genValue === "object" &&
|
|
119
|
+
!Array.isArray(genValue) &&
|
|
120
|
+
exValue !== null &&
|
|
121
|
+
typeof exValue === "object" &&
|
|
122
|
+
!Array.isArray(exValue)) {
|
|
123
|
+
// Both are objects — recurse
|
|
124
|
+
result[key] = mergeObjects(exValue, genValue);
|
|
125
|
+
}
|
|
126
|
+
else if (exValue === undefined) {
|
|
127
|
+
// Key only in generated — add it
|
|
128
|
+
result[key] = genValue;
|
|
129
|
+
}
|
|
130
|
+
// else: key exists in both and at least one is not an object — keep existing value
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/scaffold/merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* The scaffold manifest tracks generated files so re-scaffolding can
|
|
5
|
+
* intelligently merge, overwrite, or preserve content on disk.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
/** A single entry in the scaffold manifest. */
|
|
10
|
+
export interface ManifestEntry {
|
|
11
|
+
/** SHA-256 hash of the file content at generation time. */
|
|
12
|
+
hash: string;
|
|
13
|
+
/** Merge category that determines how the file is handled on re-scaffold. */
|
|
14
|
+
category: "machine" | "agent" | "script" | "shared-config";
|
|
15
|
+
}
|
|
16
|
+
/** Manifest written to disk after each scaffold run. */
|
|
17
|
+
export interface ScaffoldManifest {
|
|
18
|
+
/** Source .at file name. */
|
|
19
|
+
source: string;
|
|
20
|
+
/** SHA-256 hash of the source .at file at generation time. */
|
|
21
|
+
sourceHash: string;
|
|
22
|
+
/** Target binding name (e.g. "claude-code"). */
|
|
23
|
+
target: string;
|
|
24
|
+
/** ISO 8601 timestamp of when the scaffold was generated. */
|
|
25
|
+
generatedAt: string;
|
|
26
|
+
/** Map of relative file paths to their manifest entries. */
|
|
27
|
+
files: Record<string, ManifestEntry>;
|
|
28
|
+
}
|
|
29
|
+
/** An action to perform on a single file during incremental scaffold. */
|
|
30
|
+
export type FileAction = {
|
|
31
|
+
type: "create";
|
|
32
|
+
path: string;
|
|
33
|
+
content: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: "update";
|
|
36
|
+
path: string;
|
|
37
|
+
content: string;
|
|
38
|
+
detail: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "delete";
|
|
41
|
+
path: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: "unchanged";
|
|
44
|
+
path: string;
|
|
45
|
+
} | {
|
|
46
|
+
type: "conflict";
|
|
47
|
+
path: string;
|
|
48
|
+
content: string;
|
|
49
|
+
detail: string;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scaffold/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;CAC5D;AAED,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACtC;AAED,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the incremental scaffold system.
|
|
3
|
+
*
|
|
4
|
+
* The scaffold manifest tracks generated files so re-scaffolding can
|
|
5
|
+
* intelligently merge, overwrite, or preserve content on disk.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/scaffold/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync-back module — extracts prompt content from platform agent files
|
|
3
|
+
* and writes it back into .at file prompt {} blocks.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** A platform file with its path and content. */
|
|
7
|
+
export interface PlatformFile {
|
|
8
|
+
path: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Sync prompt content from platform agent files back into an .at source string.
|
|
13
|
+
*
|
|
14
|
+
* @param atSource - Current .at file content
|
|
15
|
+
* @param files - Platform agent files [{path, content}]
|
|
16
|
+
* @param binding - Target binding name
|
|
17
|
+
* @returns Updated .at file content with prompt {} blocks
|
|
18
|
+
*/
|
|
19
|
+
export declare function syncFromPlatform(atSource: string, files: PlatformFile[], binding: string): string;
|
|
20
|
+
/** Known structural headings in Claude Code AGENT.md files (not part of prompt content). */
|
|
21
|
+
export declare const CLAUDE_CODE_STRUCTURAL_HEADINGS: string[];
|
|
22
|
+
/** Known structural headings in Copilot .agent.md files. */
|
|
23
|
+
export declare const COPILOT_STRUCTURAL_HEADINGS: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Extract content of a markdown section until one of the known structural headings.
|
|
26
|
+
*
|
|
27
|
+
* Unlike extractSectionContent which stops at ANY heading at the same level,
|
|
28
|
+
* this only stops at headings that are part of the binding's structural format.
|
|
29
|
+
* This allows prompt content to contain arbitrary ## headings (like ## Guidelines).
|
|
30
|
+
*
|
|
31
|
+
* Skips YAML frontmatter if present.
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractSectionUntilKnownHeading(fileContent: string, heading: string, knownHeadings: string[]): string | null;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAQD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EAAE,EACrB,OAAO,EAAE,MAAM,GACd,MAAM,CAqBR;AAsCD,4FAA4F;AAC5F,eAAO,MAAM,+BAA+B,UAM3C,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,2BAA2B,UAMvC,CAAC;AA8LF;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EAAE,GACtB,MAAM,GAAG,IAAI,CAgCf"}
|