@tacuchi/agent-workflow-cli 12.8.0 → 12.10.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/dist/adapters/node-process.d.ts +4 -1
- package/dist/adapters/node-process.d.ts.map +1 -1
- package/dist/adapters/node-process.js +65 -0
- package/dist/adapters/node-process.js.map +1 -1
- package/dist/application/paths-service.d.ts +4 -0
- package/dist/application/paths-service.d.ts.map +1 -1
- package/dist/application/paths-service.js +8 -0
- package/dist/application/paths-service.js.map +1 -1
- package/dist/application/process-registry-service.d.ts +53 -0
- package/dist/application/process-registry-service.d.ts.map +1 -0
- package/dist/application/process-registry-service.js +95 -0
- package/dist/application/process-registry-service.js.map +1 -0
- package/dist/application/project-tab-data.d.ts +5 -0
- package/dist/application/project-tab-data.d.ts.map +1 -1
- package/dist/application/project-tab-data.js +20 -0
- package/dist/application/project-tab-data.js.map +1 -1
- package/dist/application/session-resume-service.d.ts +8 -0
- package/dist/application/session-resume-service.d.ts.map +1 -1
- package/dist/application/session-resume-service.js +11 -2
- package/dist/application/session-resume-service.js.map +1 -1
- package/dist/application/source-launch-scripts-service.d.ts +63 -0
- package/dist/application/source-launch-scripts-service.d.ts.map +1 -0
- package/dist/application/source-launch-scripts-service.js +302 -0
- package/dist/application/source-launch-scripts-service.js.map +1 -0
- package/dist/application/source-launch-service.d.ts +52 -0
- package/dist/application/source-launch-service.d.ts.map +1 -0
- package/dist/application/source-launch-service.js +116 -0
- package/dist/application/source-launch-service.js.map +1 -0
- package/dist/application/workspace-init-service.d.ts +3 -0
- package/dist/application/workspace-init-service.d.ts.map +1 -1
- package/dist/application/workspace-init-service.js +40 -4
- package/dist/application/workspace-init-service.js.map +1 -1
- package/dist/cli/commands/session-resume.js +6 -2
- package/dist/cli/commands/session-resume.js.map +1 -1
- package/dist/cli/tui/components/process-list.d.ts +15 -0
- package/dist/cli/tui/components/process-list.d.ts.map +1 -0
- package/dist/cli/tui/components/process-list.js +37 -0
- package/dist/cli/tui/components/process-list.js.map +1 -0
- package/dist/cli/tui/components/source-launch-form.d.ts +19 -0
- package/dist/cli/tui/components/source-launch-form.d.ts.map +1 -0
- package/dist/cli/tui/components/source-launch-form.js +68 -0
- package/dist/cli/tui/components/source-launch-form.js.map +1 -0
- package/dist/cli/tui/tabs/project-tab.d.ts.map +1 -1
- package/dist/cli/tui/tabs/project-tab.js +188 -16
- package/dist/cli/tui/tabs/project-tab.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/ports/process.d.ts +22 -0
- package/dist/ports/process.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/w/SKILL.md +15 -0
- package/skills/w/artifacts/README.md +2 -0
- package/skills/w/loops/README.md +1 -1
- package/skills/w/loops/quick-loop/SKILL.md +10 -0
- package/skills/w/loops/spec-refine-loop/SKILL.md +4 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { detectStackDict } from "./stack-detect.js";
|
|
4
|
+
const SECRET_RE = /(SECRET|TOKEN|PASSWORD|PASSWD|PWD|API[_-]?KEY|PRIVATE|CREDENTIAL)/i;
|
|
5
|
+
const MARKER_RE = /^# agent-workflow:generated v\d+ sha256=([a-f0-9]+).*$/m;
|
|
6
|
+
const MARKER_LINE_RE = /^# agent-workflow:generated v\d+ sha256=[a-f0-9]+.*\r?\n/m;
|
|
7
|
+
function sha256(text) {
|
|
8
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
9
|
+
}
|
|
10
|
+
function isSecretName(name) {
|
|
11
|
+
return SECRET_RE.test(name);
|
|
12
|
+
}
|
|
13
|
+
/** Detect the launch descriptor for a source by inspecting its files. Pure (read-only). */
|
|
14
|
+
export async function detectLaunchDescriptor(fs, sourcePath, alias) {
|
|
15
|
+
const detected = await detectStackDict(fs, sourcePath);
|
|
16
|
+
const stack = stackKey(detected);
|
|
17
|
+
const { command, args } = await deriveCommand(fs, sourcePath, stack);
|
|
18
|
+
const { params, profiles } = await parseConfig(fs, sourcePath);
|
|
19
|
+
return { version: 1, source: alias, stack, cwd: sourcePath, command, args, params, profiles };
|
|
20
|
+
}
|
|
21
|
+
function stackKey(detected) {
|
|
22
|
+
const build = (detected.build ?? "").toLowerCase();
|
|
23
|
+
if (build === "maven")
|
|
24
|
+
return "maven";
|
|
25
|
+
if (build === "gradle")
|
|
26
|
+
return "gradle";
|
|
27
|
+
if (detected.framework === "Angular")
|
|
28
|
+
return "angular";
|
|
29
|
+
if (build === "npm")
|
|
30
|
+
return "npm";
|
|
31
|
+
return "unknown";
|
|
32
|
+
}
|
|
33
|
+
async function deriveCommand(fs, sourcePath, stack) {
|
|
34
|
+
switch (stack) {
|
|
35
|
+
case "npm": {
|
|
36
|
+
const script = await pickNpmScript(fs, sourcePath);
|
|
37
|
+
if (script === "start")
|
|
38
|
+
return { command: "npm", args: ["start"] };
|
|
39
|
+
if (script)
|
|
40
|
+
return { command: "npm", args: ["run", script] };
|
|
41
|
+
return { command: null, args: [] };
|
|
42
|
+
}
|
|
43
|
+
case "angular":
|
|
44
|
+
return { command: "npm", args: ["start"] };
|
|
45
|
+
case "gradle": {
|
|
46
|
+
const wrapper = (await fs.exists(join(sourcePath, "gradlew"))) ? "./gradlew" : "gradle";
|
|
47
|
+
return { command: wrapper, args: ["bootRun"] };
|
|
48
|
+
}
|
|
49
|
+
case "maven": {
|
|
50
|
+
const wrapper = (await fs.exists(join(sourcePath, "mvnw"))) ? "./mvnw" : "mvn";
|
|
51
|
+
return { command: wrapper, args: ["spring-boot:run"] };
|
|
52
|
+
}
|
|
53
|
+
default:
|
|
54
|
+
return { command: null, args: [] };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Prefer a `dev` script, then `start`; null when package.json has neither. */
|
|
58
|
+
async function pickNpmScript(fs, sourcePath) {
|
|
59
|
+
try {
|
|
60
|
+
const pkg = JSON.parse(await fs.readText(join(sourcePath, "package.json")));
|
|
61
|
+
const scripts = pkg.scripts ?? {};
|
|
62
|
+
if (scripts.dev)
|
|
63
|
+
return "dev";
|
|
64
|
+
if (scripts.start)
|
|
65
|
+
return "start";
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Parse `.env*` (and detect Spring `application-<profile>.*`) into params + profiles. */
|
|
73
|
+
async function parseConfig(fs, sourcePath) {
|
|
74
|
+
let entries = [];
|
|
75
|
+
try {
|
|
76
|
+
entries = await fs.list(sourcePath);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { params: [], profiles: [] };
|
|
80
|
+
}
|
|
81
|
+
const names = entries.map((e) => e.name);
|
|
82
|
+
const profiles = detectProfiles(names);
|
|
83
|
+
// Params: union of keys across `.env` and `.env.<profile>` files; default from base `.env`.
|
|
84
|
+
const params = new Map();
|
|
85
|
+
const envFiles = names.filter((n) => n === ".env" || /^\.env\.[A-Za-z0-9_-]+$/.test(n));
|
|
86
|
+
for (const file of envFiles) {
|
|
87
|
+
let content;
|
|
88
|
+
try {
|
|
89
|
+
content = await fs.readText(join(sourcePath, file));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
mergeEnvParams(params, parseEnv(content), file === ".env");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
params: [...params.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
|
98
|
+
profiles: profiles.sort(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Profiles from `.env.<profile>` (excluding `.env.local`) and `application-<profile>.{yml,yaml,properties}`. */
|
|
102
|
+
function detectProfiles(names) {
|
|
103
|
+
const profiles = new Set();
|
|
104
|
+
for (const name of names) {
|
|
105
|
+
const envProfile = /^\.env\.([A-Za-z0-9_-]+)$/.exec(name)?.[1];
|
|
106
|
+
if (envProfile && envProfile !== "local")
|
|
107
|
+
profiles.add(envProfile);
|
|
108
|
+
const springProfile = /^application-([A-Za-z0-9_-]+)\.(ya?ml|properties)$/.exec(name)?.[1];
|
|
109
|
+
if (springProfile)
|
|
110
|
+
profiles.add(springProfile);
|
|
111
|
+
}
|
|
112
|
+
return [...profiles];
|
|
113
|
+
}
|
|
114
|
+
/** Merge one env file's pairs into the param map; the base `.env` supplies defaults. */
|
|
115
|
+
function mergeEnvParams(params, pairs, isBase) {
|
|
116
|
+
for (const [key, value] of pairs) {
|
|
117
|
+
const existing = params.get(key);
|
|
118
|
+
if (!existing) {
|
|
119
|
+
params.set(key, { name: key, default: isBase ? value : "", secret: isSecretName(key) });
|
|
120
|
+
}
|
|
121
|
+
else if (isBase && existing.default === "") {
|
|
122
|
+
existing.default = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function parseEnv(content) {
|
|
127
|
+
const out = [];
|
|
128
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
129
|
+
const line = raw.trim();
|
|
130
|
+
if (!line || line.startsWith("#"))
|
|
131
|
+
continue;
|
|
132
|
+
const eq = line.indexOf("=");
|
|
133
|
+
if (eq <= 0)
|
|
134
|
+
continue;
|
|
135
|
+
const key = line.slice(0, eq).trim();
|
|
136
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
137
|
+
continue;
|
|
138
|
+
let value = line.slice(eq + 1).trim();
|
|
139
|
+
// Strip surrounding quotes; never carry a secret's value into the descriptor.
|
|
140
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
141
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
142
|
+
value = value.slice(1, -1);
|
|
143
|
+
}
|
|
144
|
+
out.push([key, isSecretName(key) ? "" : value]);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
// --- Rendering -------------------------------------------------------------
|
|
149
|
+
/**
|
|
150
|
+
* Assemble a generated script with a self-describing hash marker. The recorded
|
|
151
|
+
* hash covers the whole file EXCEPT the marker line itself (a line cannot hash
|
|
152
|
+
* its own value), so regeneration can detect user edits to any other line.
|
|
153
|
+
*/
|
|
154
|
+
function withMarker(beforeMarker, afterMarker) {
|
|
155
|
+
const hashed = `${[...beforeMarker, ...afterMarker].join("\n")}\n`;
|
|
156
|
+
const marker = `# agent-workflow:generated v1 sha256=${sha256(hashed)}`;
|
|
157
|
+
return `${[...beforeMarker, marker, ...afterMarker].join("\n")}\n`;
|
|
158
|
+
}
|
|
159
|
+
function humanHeader(desc) {
|
|
160
|
+
return [
|
|
161
|
+
`# Lanzamiento directo de ${desc.source} (${desc.stack}).`,
|
|
162
|
+
"# Editá libremente debajo: agent-workflow NO re-genera scripts que hayas editado.",
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
export function renderLaunchJson(desc) {
|
|
166
|
+
const hash = sha256(JSON.stringify(desc, null, 2));
|
|
167
|
+
const withMeta = { ...desc, _generated: { version: 1, sha256: hash } };
|
|
168
|
+
return `${JSON.stringify(withMeta, null, 2)}\n`;
|
|
169
|
+
}
|
|
170
|
+
export function renderRunSh(desc) {
|
|
171
|
+
const after = [
|
|
172
|
+
...humanHeader(desc),
|
|
173
|
+
"set -euo pipefail",
|
|
174
|
+
`cd "${desc.cwd}"`,
|
|
175
|
+
'PROFILE="${1:-}"',
|
|
176
|
+
'if [ -n "$PROFILE" ] && [ -f ".env.$PROFILE" ]; then set -a; . "./.env.$PROFILE"; set +a; fi',
|
|
177
|
+
];
|
|
178
|
+
for (const p of desc.params) {
|
|
179
|
+
after.push(`export ${p.name}="\${${p.name}:-${shEscape(p.default)}}"`);
|
|
180
|
+
}
|
|
181
|
+
if (desc.command) {
|
|
182
|
+
after.push(`exec ${desc.command} ${desc.args.map(shQuote).join(" ")}`.trimEnd());
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
after.push(`echo "No se detectó comando de arranque para ${desc.source}. Completá este script." >&2`);
|
|
186
|
+
after.push("exit 1");
|
|
187
|
+
}
|
|
188
|
+
return withMarker(["#!/usr/bin/env bash"], after);
|
|
189
|
+
}
|
|
190
|
+
export function renderRunPs1(desc) {
|
|
191
|
+
const after = [
|
|
192
|
+
...humanHeader(desc),
|
|
193
|
+
'param([string]$Profile = "")',
|
|
194
|
+
"$ErrorActionPreference = 'Stop'",
|
|
195
|
+
`Set-Location "${desc.cwd}"`,
|
|
196
|
+
'if ($Profile -and (Test-Path ".env.$Profile")) { Get-Content ".env.$Profile" | ForEach-Object { if ($_ -match "^\\s*([^#=]+)=(.*)$") { [Environment]::SetEnvironmentVariable($matches[1].Trim(), $matches[2].Trim()) } } }',
|
|
197
|
+
];
|
|
198
|
+
for (const p of desc.params) {
|
|
199
|
+
after.push(`if (-not $env:${p.name}) { $env:${p.name} = "${ps1Escape(p.default)}" }`);
|
|
200
|
+
}
|
|
201
|
+
if (desc.command) {
|
|
202
|
+
after.push(`& ${ps1Quote(desc.command)} ${desc.args.map(ps1Quote).join(" ")}`.trimEnd());
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
after.push(`Write-Error "No se detectó comando de arranque para ${desc.source}. Completá este script."`);
|
|
206
|
+
after.push("exit 1");
|
|
207
|
+
}
|
|
208
|
+
return withMarker([], after);
|
|
209
|
+
}
|
|
210
|
+
function shEscape(v) {
|
|
211
|
+
return v.replace(/(["\\$`])/g, "\\$1");
|
|
212
|
+
}
|
|
213
|
+
function shQuote(v) {
|
|
214
|
+
return /^[A-Za-z0-9_./:-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
|
|
215
|
+
}
|
|
216
|
+
function ps1Escape(v) {
|
|
217
|
+
return v.replace(/`/g, "``").replace(/"/g, '`"');
|
|
218
|
+
}
|
|
219
|
+
function ps1Quote(v) {
|
|
220
|
+
return /^[A-Za-z0-9_./:-]+$/.test(v) ? v : `"${ps1Escape(v)}"`;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Write `content` to `path` only if the on-disk file is still pristine (its
|
|
224
|
+
* recorded hash matches its current body). A user-edited file is preserved.
|
|
225
|
+
* `extractRecorded` returns the recorded hash + the hash of the current body,
|
|
226
|
+
* or null when the file has no marker (treated as user-owned → preserved).
|
|
227
|
+
*/
|
|
228
|
+
async function writeIfPristine(fs, path, content, recompute) {
|
|
229
|
+
if (!(await fs.exists(path))) {
|
|
230
|
+
await fs.writeText(path, content);
|
|
231
|
+
return "created";
|
|
232
|
+
}
|
|
233
|
+
const existing = await fs.readText(path);
|
|
234
|
+
const check = recompute(existing);
|
|
235
|
+
if (check && check.recorded === check.actual) {
|
|
236
|
+
await fs.writeText(path, content);
|
|
237
|
+
return "regenerated";
|
|
238
|
+
}
|
|
239
|
+
return "preserved";
|
|
240
|
+
}
|
|
241
|
+
function shellRecompute(existing) {
|
|
242
|
+
const m = MARKER_RE.exec(existing);
|
|
243
|
+
if (!m)
|
|
244
|
+
return null;
|
|
245
|
+
const recorded = m[1];
|
|
246
|
+
// Hash covers the whole file minus the marker line (symmetric with withMarker).
|
|
247
|
+
const withoutMarker = existing.replace(MARKER_LINE_RE, "");
|
|
248
|
+
return { recorded, actual: sha256(withoutMarker) };
|
|
249
|
+
}
|
|
250
|
+
function jsonRecompute(existing) {
|
|
251
|
+
try {
|
|
252
|
+
const parsed = JSON.parse(existing);
|
|
253
|
+
const recorded = parsed._generated?.sha256;
|
|
254
|
+
if (typeof recorded !== "string")
|
|
255
|
+
return null;
|
|
256
|
+
const { _generated, ...rest } = parsed;
|
|
257
|
+
return { recorded, actual: sha256(JSON.stringify(rest, null, 2)) };
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/** Generate (idempotently) the descriptor + per-OS scripts for one source. */
|
|
264
|
+
export async function generateSourceLaunchArtifacts(fs, toolsDir, sourcePath, alias) {
|
|
265
|
+
const desc = await detectLaunchDescriptor(fs, sourcePath, alias);
|
|
266
|
+
const dir = join(toolsDir, alias);
|
|
267
|
+
await fs.mkdirp(dir);
|
|
268
|
+
const launchJson = await writeIfPristine(fs, join(dir, "launch.json"), renderLaunchJson(desc), jsonRecompute);
|
|
269
|
+
const runSh = await writeIfPristine(fs, join(dir, "run.sh"), renderRunSh(desc), shellRecompute);
|
|
270
|
+
const runPs1 = await writeIfPristine(fs, join(dir, "run.ps1"), renderRunPs1(desc), shellRecompute);
|
|
271
|
+
return {
|
|
272
|
+
alias,
|
|
273
|
+
stack: desc.stack,
|
|
274
|
+
launchable: desc.command !== null,
|
|
275
|
+
outcomes: { launchJson, runSh, runPs1 },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Generate launch artifacts for every source under `toolsDir/<alias>/`. Degrades
|
|
280
|
+
* cleanly: with the `tools` capability off, nothing is generated (and it says so);
|
|
281
|
+
* a source whose path is missing is skipped rather than producing junk.
|
|
282
|
+
*/
|
|
283
|
+
export async function generateLaunchArtifacts(fs, toolsDir, sources, toolsEnabled) {
|
|
284
|
+
if (!toolsEnabled) {
|
|
285
|
+
return {
|
|
286
|
+
toolsRole: "off",
|
|
287
|
+
generated: [],
|
|
288
|
+
skipped: sources.map((s) => ({ alias: s.alias, reason: "tools_role_off" })),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const generated = [];
|
|
292
|
+
const skipped = [];
|
|
293
|
+
for (const s of sources) {
|
|
294
|
+
if (!(await fs.exists(s.path))) {
|
|
295
|
+
skipped.push({ alias: s.alias, reason: "path_not_found" });
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
generated.push(await generateSourceLaunchArtifacts(fs, toolsDir, s.path, s.alias));
|
|
299
|
+
}
|
|
300
|
+
return { toolsRole: "enabled", generated, skipped };
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=source-launch-scripts-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-launch-scripts-service.js","sourceRoot":"","sources":["../../src/application/source-launch-scripts-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { FileSystemPort } from "../ports/file-system.js";
|
|
2
|
+
import type { ProcessPort } from "../ports/process.js";
|
|
3
|
+
import type { PathsService } from "./paths-service.js";
|
|
4
|
+
import { type ProcessRecord } from "./process-registry-service.js";
|
|
5
|
+
import type { LaunchDescriptor } from "./source-launch-scripts-service.js";
|
|
6
|
+
export interface LaunchRequest {
|
|
7
|
+
alias: string;
|
|
8
|
+
profile: string | null;
|
|
9
|
+
/** param name → value entered by the user (overrides the descriptor default). */
|
|
10
|
+
values: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedLaunch {
|
|
13
|
+
command: string;
|
|
14
|
+
args: string[];
|
|
15
|
+
cwd: string;
|
|
16
|
+
env: Record<string, string>;
|
|
17
|
+
logPath: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LaunchDeps {
|
|
20
|
+
fs: FileSystemPort;
|
|
21
|
+
proc: ProcessPort;
|
|
22
|
+
paths: PathsService;
|
|
23
|
+
/** Base environment the child inherits (the caller passes the real process env). */
|
|
24
|
+
baseEnv: Record<string, string>;
|
|
25
|
+
/** Injected clock for deterministic tests; defaults to the wall clock. */
|
|
26
|
+
now?: () => string;
|
|
27
|
+
}
|
|
28
|
+
export type LaunchResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
record: ProcessRecord;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
error: "no_descriptor" | "not_launchable" | "spawn_failed";
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
/** Read a source's launch descriptor, or null when absent/corrupt. */
|
|
37
|
+
export declare function readDescriptor(fs: FileSystemPort, workspaceDir: string, alias: string): Promise<LaunchDescriptor | null>;
|
|
38
|
+
/** Log file for a source+profile, under docs/logs/. */
|
|
39
|
+
export declare function logFileFor(logsDir: string, alias: string, profile: string | null): string;
|
|
40
|
+
/** Resolve the concrete command/args/env/cwd/log for a launch. Null if the descriptor has no command. */
|
|
41
|
+
export declare function resolveLaunch(desc: LaunchDescriptor, req: LaunchRequest, logsDir: string, baseEnv: Record<string, string>): ResolvedLaunch | null;
|
|
42
|
+
/** A running process for the same source+profile, if any (collision). */
|
|
43
|
+
export declare function findCollision(processes: ProcessRecord[], alias: string, profile: string | null): ProcessRecord | undefined;
|
|
44
|
+
/** Launch a source detached: resolve → open log dir → spawnDetached → register. */
|
|
45
|
+
export declare function launchSource(deps: LaunchDeps, req: LaunchRequest): Promise<LaunchResult>;
|
|
46
|
+
/** Stop a process (kill its tree) and mark it stopped in the registry. */
|
|
47
|
+
export declare function stopProcess(deps: LaunchDeps, record: ProcessRecord): Promise<void>;
|
|
48
|
+
/** Relaunch = stop the existing process, then launch the same source+profile afresh. */
|
|
49
|
+
export declare function relaunchProcess(deps: LaunchDeps, record: ProcessRecord): Promise<LaunchResult>;
|
|
50
|
+
/** Last `maxLines` of a process log (best-effort; empty when missing). */
|
|
51
|
+
export declare function tailLog(fs: FileSystemPort, logPath: string, maxLines: number): Promise<string[]>;
|
|
52
|
+
//# sourceMappingURL=source-launch-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-launch-service.d.ts","sourceRoot":"","sources":["../../src/application/source-launch-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,KAAK,aAAa,EAA0B,MAAM,+BAA+B,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,cAAc,CAAC;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F,sEAAsE;AACtE,wBAAsB,cAAc,CAClC,EAAE,EAAE,cAAc,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAQlC;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEzF;AAED,yGAAyG;AACzG,wBAAgB,aAAa,CAC3B,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,cAAc,GAAG,IAAI,CAcvB;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,SAAS,EAAE,aAAa,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,aAAa,GAAG,SAAS,CAI3B;AAMD,mFAAmF;AACnF,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAyC9F;AAED,0EAA0E;AAC1E,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxF;AAED,wFAAwF;AACxF,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,0EAA0E;AAC1E,wBAAsB,OAAO,CAC3B,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CAUnB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { ProcessRegistryService } from "./process-registry-service.js";
|
|
3
|
+
/** Read a source's launch descriptor, or null when absent/corrupt. */
|
|
4
|
+
export async function readDescriptor(fs, workspaceDir, alias) {
|
|
5
|
+
const file = join(workspaceDir, "docs", "tools", alias, "launch.json");
|
|
6
|
+
if (!(await fs.exists(file)))
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(await fs.readText(file));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Log file for a source+profile, under docs/logs/. */
|
|
16
|
+
export function logFileFor(logsDir, alias, profile) {
|
|
17
|
+
return join(logsDir, profile ? `${alias}-${profile}.log` : `${alias}.log`);
|
|
18
|
+
}
|
|
19
|
+
/** Resolve the concrete command/args/env/cwd/log for a launch. Null if the descriptor has no command. */
|
|
20
|
+
export function resolveLaunch(desc, req, logsDir, baseEnv) {
|
|
21
|
+
if (!desc.command)
|
|
22
|
+
return null;
|
|
23
|
+
const env = { ...baseEnv };
|
|
24
|
+
for (const p of desc.params) {
|
|
25
|
+
env[p.name] = req.values[p.name] ?? p.default;
|
|
26
|
+
}
|
|
27
|
+
if (req.profile)
|
|
28
|
+
env.PROFILE = req.profile;
|
|
29
|
+
return {
|
|
30
|
+
command: desc.command,
|
|
31
|
+
args: desc.args,
|
|
32
|
+
cwd: desc.cwd,
|
|
33
|
+
env,
|
|
34
|
+
logPath: logFileFor(logsDir, req.alias, req.profile),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** A running process for the same source+profile, if any (collision). */
|
|
38
|
+
export function findCollision(processes, alias, profile) {
|
|
39
|
+
return processes.find((p) => p.sourceAlias === alias && p.profile === profile && p.state === "running");
|
|
40
|
+
}
|
|
41
|
+
function registry(deps) {
|
|
42
|
+
return new ProcessRegistryService(deps.fs, deps.proc, deps.paths.cwdProcessesFile());
|
|
43
|
+
}
|
|
44
|
+
/** Launch a source detached: resolve → open log dir → spawnDetached → register. */
|
|
45
|
+
export async function launchSource(deps, req) {
|
|
46
|
+
const desc = await readDescriptor(deps.fs, deps.paths.workspaceDir(), req.alias);
|
|
47
|
+
if (!desc)
|
|
48
|
+
return { ok: false, error: "no_descriptor", message: `Sin descriptor para ${req.alias}` };
|
|
49
|
+
const logsDir = deps.paths.cwdDocsLogsDir();
|
|
50
|
+
const resolved = resolveLaunch(desc, req, logsDir, deps.baseEnv);
|
|
51
|
+
if (!resolved) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: "not_launchable",
|
|
55
|
+
message: `El descriptor de ${req.alias} no tiene comando de arranque`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
await deps.fs.mkdirp(logsDir);
|
|
59
|
+
try {
|
|
60
|
+
const { pid } = await deps.proc.spawnDetached(resolved.command, resolved.args, {
|
|
61
|
+
cwd: resolved.cwd,
|
|
62
|
+
env: resolved.env,
|
|
63
|
+
logPath: resolved.logPath,
|
|
64
|
+
});
|
|
65
|
+
const startedAt = (deps.now ?? (() => new Date().toISOString()))();
|
|
66
|
+
// Persist only NON-secret entered values so a relaunch can reuse them; secrets
|
|
67
|
+
// are never written to the registry.
|
|
68
|
+
const secretNames = new Set(desc.params.filter((p) => p.secret).map((p) => p.name));
|
|
69
|
+
const persistedValues = Object.fromEntries(Object.entries(req.values).filter(([k]) => !secretNames.has(k)));
|
|
70
|
+
const record = await registry(deps).register({
|
|
71
|
+
sourceAlias: req.alias,
|
|
72
|
+
profile: req.profile,
|
|
73
|
+
command: resolved.command,
|
|
74
|
+
args: resolved.args,
|
|
75
|
+
pid,
|
|
76
|
+
startedAt,
|
|
77
|
+
logPath: resolved.logPath,
|
|
78
|
+
values: persistedValues,
|
|
79
|
+
});
|
|
80
|
+
return { ok: true, record };
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { ok: false, error: "spawn_failed", message: err.message };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Stop a process (kill its tree) and mark it stopped in the registry. */
|
|
87
|
+
export async function stopProcess(deps, record) {
|
|
88
|
+
await deps.proc.killTree(record.pid);
|
|
89
|
+
await registry(deps).markStopped(record.id);
|
|
90
|
+
}
|
|
91
|
+
/** Relaunch = stop the existing process, then launch the same source+profile afresh. */
|
|
92
|
+
export async function relaunchProcess(deps, record) {
|
|
93
|
+
await stopProcess(deps, record);
|
|
94
|
+
// Reuse the same profile + persisted non-secret values (secrets re-default to empty).
|
|
95
|
+
return launchSource(deps, {
|
|
96
|
+
alias: record.sourceAlias,
|
|
97
|
+
profile: record.profile,
|
|
98
|
+
values: record.values ?? {},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** Last `maxLines` of a process log (best-effort; empty when missing). */
|
|
102
|
+
export async function tailLog(fs, logPath, maxLines) {
|
|
103
|
+
if (!(await fs.exists(logPath)))
|
|
104
|
+
return [];
|
|
105
|
+
try {
|
|
106
|
+
const lines = (await fs.readText(logPath)).split(/\r?\n/);
|
|
107
|
+
// Drop only the trailing empty line from a final newline; keep interior blanks.
|
|
108
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
109
|
+
lines.pop();
|
|
110
|
+
return lines.slice(-maxLines);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=source-launch-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-launch-service.js","sourceRoot":"","sources":["../../src/application/source-launch-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import type { FileSystemPort } from "../ports/file-system.js";
|
|
|
3
3
|
import { type MultirootError, type MultirootResult } from "./multiroot-service.js";
|
|
4
4
|
import { PathsService } from "./paths-service.js";
|
|
5
5
|
import { type ProjectMdUpsertError, type ProjectMdUpsertOutput } from "./project-md-upsert-service.js";
|
|
6
|
+
import { type LaunchArtifactsSummary } from "./source-launch-scripts-service.js";
|
|
6
7
|
export interface WorkspaceSource {
|
|
7
8
|
alias: string;
|
|
8
9
|
path: string;
|
|
@@ -48,6 +49,8 @@ export interface WorkspaceInitResult {
|
|
|
48
49
|
};
|
|
49
50
|
/** Reconcile: detach of sources that were in the previous block and no longer are. */
|
|
50
51
|
detached_removed?: MultirootResult | MultirootError;
|
|
52
|
+
/** Per-source launch descriptor + scripts generated under docs/tools/ (gated on the `tools` role). */
|
|
53
|
+
launch_artifacts: LaunchArtifactsSummary;
|
|
51
54
|
}
|
|
52
55
|
/**
|
|
53
56
|
* Initialize the current directory as an agent-workflow **workspace**. Unifies the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-init-service.d.ts","sourceRoot":"","sources":["../../src/application/workspace-init-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAgB,MAAM,wBAAwB,CAAC;AAGjG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE3B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-init-service.d.ts","sourceRoot":"","sources":["../../src/application/workspace-init-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAgB,MAAM,wBAAwB,CAAC;AAGjG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,oCAAoC,CAAC;AAuB5C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,CAAC;IAC1B,WAAW,EAAE,SAAS,GAAG,QAAQ,CAAC;IAClC,UAAU,EAAE,qBAAqB,GAAG,oBAAoB,CAAC;IACzD,+EAA+E;IAC/E,gBAAgB,EAAE,eAAe,GAAG,cAAc,GAAG;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC;IACpD,sGAAsG;IACtG,gBAAgB,EAAE,sBAAsB,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,GAAG,uBAAuB,CAAC,CAiGxD"}
|
|
@@ -5,6 +5,8 @@ import { normalizePath } from "./multiroot/paths.js";
|
|
|
5
5
|
import { parseProjectBlock } from "./parsers/project-block.js";
|
|
6
6
|
import { PathsService } from "./paths-service.js";
|
|
7
7
|
import { runProjectMdUpsertWrite, } from "./project-md-upsert-service.js";
|
|
8
|
+
import { resolveSkills } from "./skills-resolver-service.js";
|
|
9
|
+
import { generateLaunchArtifacts, } from "./source-launch-scripts-service.js";
|
|
8
10
|
/** docs/ taxonomy scaffolded for every workspace (one folder per export category). */
|
|
9
11
|
const DOCS_FOLDERS = [
|
|
10
12
|
"specs",
|
|
@@ -17,6 +19,10 @@ const DOCS_FOLDERS = [
|
|
|
17
19
|
];
|
|
18
20
|
/** Visibility files (machine-specific absolute roots) — gitignored when external sources exist. */
|
|
19
21
|
const VISIBILITY_GITIGNORE = [".claude/settings.local.json", ".codex/config.toml"];
|
|
22
|
+
/** Runtime artifacts of the source-launch feature — machine-specific, never committed. */
|
|
23
|
+
function runtimeGitignoreEntries(ns) {
|
|
24
|
+
return [`.${ns}/processes.json`, "docs/logs/"];
|
|
25
|
+
}
|
|
20
26
|
const DEFAULT_MAIN_BRANCH = "main";
|
|
21
27
|
/**
|
|
22
28
|
* Initialize the current directory as an agent-workflow **workspace**. Unifies the
|
|
@@ -49,6 +55,12 @@ export async function runWorkspaceInit(fs, env, paths, input) {
|
|
|
49
55
|
const targetEnv = workspace !== resolve(env.cwd()) ? overrideCwd(env, workspace) : env;
|
|
50
56
|
const scaffold = await scaffoldDirs(fs, workspace, wsPaths);
|
|
51
57
|
const skillsToml = await seedSkillsToml(fs, wsPaths);
|
|
58
|
+
// Runtime artifacts (process registry + launch logs) are machine-specific — gitignore always.
|
|
59
|
+
await ensureRuntimeGitignore(fs, workspace, wsPaths.namespace);
|
|
60
|
+
// Per-source launch artifacts under docs/tools/ (descriptor + run.sh/run.ps1),
|
|
61
|
+
// gated on the `tools` capability and idempotent (preserves user-edited scripts).
|
|
62
|
+
const skillsRes = await resolveSkills(fs, wsPaths);
|
|
63
|
+
const launchArtifacts = await generateLaunchArtifacts(fs, join(workspace, "docs", "tools"), sources.map((s) => ({ alias: s.alias, path: s.path })), skillsRes.skills.tools.enabled);
|
|
52
64
|
// Previous sources (to detach removed ones) come from the same existing block.
|
|
53
65
|
const previousPaths = (existing?.fuentes ?? []).map((f) => f.path).filter((p) => p.length > 0);
|
|
54
66
|
const projectMd = await runProjectMdUpsertWrite(fs, targetEnv, wsPaths, {
|
|
@@ -79,6 +91,7 @@ export async function runWorkspaceInit(fs, env, paths, input) {
|
|
|
79
91
|
skills_toml: skillsToml,
|
|
80
92
|
project_md: projectMd,
|
|
81
93
|
attach_multiroot: { skipped: true, reason: "project_md_failed" },
|
|
94
|
+
launch_artifacts: launchArtifacts,
|
|
82
95
|
};
|
|
83
96
|
}
|
|
84
97
|
// Multi-root visibility only matters with 2+ sources. Single source → no-op.
|
|
@@ -93,6 +106,7 @@ export async function runWorkspaceInit(fs, env, paths, input) {
|
|
|
93
106
|
project_md: projectMd,
|
|
94
107
|
attach_multiroot: visibility.attach,
|
|
95
108
|
...(visibility.detached !== undefined ? { detached_removed: visibility.detached } : {}),
|
|
109
|
+
launch_artifacts: launchArtifacts,
|
|
96
110
|
};
|
|
97
111
|
}
|
|
98
112
|
function plannedScaffold(workspace, ns) {
|
|
@@ -114,6 +128,11 @@ function buildDryRunResult(workspace, namespace, sources) {
|
|
|
114
128
|
attach_multiroot: anyExternal
|
|
115
129
|
? { skipped: true, reason: "dry_run" }
|
|
116
130
|
: { skipped: true, reason: "no_external_sources" },
|
|
131
|
+
launch_artifacts: {
|
|
132
|
+
toolsRole: "enabled",
|
|
133
|
+
generated: [],
|
|
134
|
+
skipped: sources.map((s) => ({ alias: s.alias, reason: "path_not_found" })),
|
|
135
|
+
},
|
|
117
136
|
};
|
|
118
137
|
}
|
|
119
138
|
async function scaffoldDirs(fs, workspace, wsPaths) {
|
|
@@ -132,6 +151,15 @@ async function scaffoldDirs(fs, workspace, wsPaths) {
|
|
|
132
151
|
await fs.writeText(keep, "");
|
|
133
152
|
created.push(dir);
|
|
134
153
|
}
|
|
154
|
+
// Runtime launch logs — created (no .gitkeep: the folder is gitignored).
|
|
155
|
+
const logsDir = join(workspace, "docs", "logs");
|
|
156
|
+
if (await fs.exists(logsDir)) {
|
|
157
|
+
existing.push(logsDir);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
await fs.mkdirp(logsDir);
|
|
161
|
+
created.push(logsDir);
|
|
162
|
+
}
|
|
135
163
|
return { created, existing };
|
|
136
164
|
}
|
|
137
165
|
/** Seed `.workflow/skills.toml` with a commented template. Never clobbers an existing file. */
|
|
@@ -240,21 +268,29 @@ function resolveProyecto(arg, existing, workspace) {
|
|
|
240
268
|
return existing.trim();
|
|
241
269
|
return basename(workspace);
|
|
242
270
|
}
|
|
243
|
-
/**
|
|
244
|
-
async function
|
|
271
|
+
/** Append any missing entries under a header to the workspace `.gitignore` (idempotent). */
|
|
272
|
+
async function appendGitignoreEntries(fs, workspace, header, entries) {
|
|
245
273
|
const file = join(workspace, ".gitignore");
|
|
246
274
|
const existing = (await fs.exists(file)) ? await fs.readText(file) : "";
|
|
247
275
|
const present = new Set(existing
|
|
248
276
|
.split(/\r?\n/)
|
|
249
277
|
.map((l) => l.trim())
|
|
250
278
|
.filter((l) => l.length > 0));
|
|
251
|
-
const missing =
|
|
279
|
+
const missing = entries.filter((e) => !present.has(e));
|
|
252
280
|
if (missing.length === 0)
|
|
253
281
|
return;
|
|
254
|
-
const block =
|
|
282
|
+
const block = `${header}\n${missing.join("\n")}\n`;
|
|
255
283
|
const body = existing.replace(/\s+$/, "");
|
|
256
284
|
await fs.writeText(file, body.length === 0 ? block : `${body}\n\n${block}`);
|
|
257
285
|
}
|
|
286
|
+
/** Ensure the workspace `.gitignore` ignores the visibility files (idempotent). */
|
|
287
|
+
async function ensureVisibilityGitignore(fs, workspace) {
|
|
288
|
+
await appendGitignoreEntries(fs, workspace, "# Multi-root visibility (machine-specific paths — do not commit)", VISIBILITY_GITIGNORE);
|
|
289
|
+
}
|
|
290
|
+
/** Ensure the workspace `.gitignore` ignores source-launch runtime artifacts (idempotent, always). */
|
|
291
|
+
async function ensureRuntimeGitignore(fs, workspace, ns) {
|
|
292
|
+
await appendGitignoreEntries(fs, workspace, "# agent-workflow runtime (machine-specific — do not commit)", runtimeGitignoreEntries(ns));
|
|
293
|
+
}
|
|
258
294
|
function validateSources(sources) {
|
|
259
295
|
if (!sources || sources.length < 1) {
|
|
260
296
|
return {
|