@codyswann/lisa 4.60.11 → 4.61.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.
Files changed (91) hide show
  1. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +15 -13
  2. package/all/copy-overwrite/scripts/lib/windows-process-job.cs +249 -0
  3. package/all/copy-overwrite/scripts/lib/windows-process-job.mjs +119 -0
  4. package/all/copy-overwrite/scripts/lib/windows-process-job.ps1 +25 -0
  5. package/dist/cli/starter-sync-content.d.ts +25 -0
  6. package/dist/cli/starter-sync-content.d.ts.map +1 -0
  7. package/dist/cli/starter-sync-content.js +106 -0
  8. package/dist/cli/starter-sync-content.js.map +1 -0
  9. package/dist/cli/starter-sync-source.d.ts +33 -0
  10. package/dist/cli/starter-sync-source.d.ts.map +1 -0
  11. package/dist/cli/starter-sync-source.js +154 -0
  12. package/dist/cli/starter-sync-source.js.map +1 -0
  13. package/dist/cli/starter-sync-write.d.ts +11 -0
  14. package/dist/cli/starter-sync-write.d.ts.map +1 -0
  15. package/dist/cli/starter-sync-write.js +126 -0
  16. package/dist/cli/starter-sync-write.js.map +1 -0
  17. package/dist/cli/starter-sync.d.ts +22 -0
  18. package/dist/cli/starter-sync.d.ts.map +1 -0
  19. package/dist/cli/starter-sync.js +219 -0
  20. package/dist/cli/starter-sync.js.map +1 -0
  21. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  22. package/dist/core/lisa-owned-hash-ledger.js +40 -0
  23. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  24. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  25. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  26. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  27. package/dist/core/upstream-evidence-manifest.js +21 -2
  28. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  29. package/dist/health/template-inspection.d.ts +16 -1
  30. package/dist/health/template-inspection.d.ts.map +1 -1
  31. package/dist/health/template-inspection.js +1 -1
  32. package/dist/health/template-inspection.js.map +1 -1
  33. package/dist/strategies/copy-contents.d.ts +11 -0
  34. package/dist/strategies/copy-contents.d.ts.map +1 -1
  35. package/dist/strategies/copy-contents.js +21 -5
  36. package/dist/strategies/copy-contents.js.map +1 -1
  37. package/dist/utils/atomic-file-write.d.ts +1 -1
  38. package/dist/utils/atomic-file-write.d.ts.map +1 -1
  39. package/dist/utils/atomic-file-write.js.map +1 -1
  40. package/package.json +4 -4
  41. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-agy/plugin.json +1 -1
  44. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  47. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-expo-agy/plugin.json +1 -1
  54. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  59. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  64. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  69. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  74. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-rails-agy/plugin.json +1 -1
  79. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  84. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  89. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  91. package/scripts/two-channel-couplings.json +1 -1
@@ -3,14 +3,18 @@
3
3
  // Do not edit directly — durable changes belong upstream in Lisa.
4
4
 
5
5
  /**
6
- * Run one shell command in its own process group and reap the whole group.
6
+ * Run one shell command in a process boundary and reap every descendant.
7
7
  *
8
8
  * Node's synchronous timeout signals only the direct child. A gate command is
9
9
  * a tree (shell, package manager, test workers), so killing only the shell
10
10
  * leaves descendants running against scratch state the caller then removes.
11
11
  * This small asynchronous supervisor is invoked through a synchronous parent:
12
12
  * it owns the process-group id, applies the deadline, terminates the group, and
13
- * only then lets the parent continue.
13
+ * only then lets the parent continue. On Windows, the common PowerShell helper
14
+ * owns a native Job Object because a shell PID cannot identify descendants
15
+ * after the shell exits. Windows 10 / Server 2016 or newer and the installed
16
+ * Windows PowerShell execution policy must permit that helper; startup failure
17
+ * rejects the command without an uncontained fallback.
14
18
  *
15
19
  * ## Interrupting a run
16
20
  *
@@ -31,6 +35,7 @@
31
35
  import { spawn, spawnSync } from "node:child_process";
32
36
 
33
37
  import { invokedAsScript } from "./invoked-as-script.mjs";
38
+ import { startWindowsProcessJob } from "./windows-process-job.mjs";
34
39
 
35
40
  const KILL_GRACE_MS = 750;
36
41
  const REAP_POLL_MS = 25;
@@ -407,7 +412,7 @@ export async function reapTree(pid, controls = DEFAULT_REAP_CONTROLS) {
407
412
  * lands on a recycled pid.
408
413
  * @param {string} command Shell source to supervise.
409
414
  * @param {number} timeoutMs Deadline for the whole tree.
410
- * @param {typeof reapTree} [reap] Injectable group reaper.
415
+ * @param {typeof reapTree} [reap] Injectable POSIX group reaper. Windows retains its native job owner.
411
416
  * @param {object} [options] Interrupt-watch seams, injectable for tests.
412
417
  * @param {readonly number[]} [options.watchPids] Pids whose death ends this run.
413
418
  * @param {typeof interruptionReason} [options.detect] Interrupt predicate.
@@ -425,14 +430,7 @@ export function supervise(command, timeoutMs, reap = reapTree, options = {}) {
425
430
  launchParentPid = process.ppid,
426
431
  } = options;
427
432
  return new Promise((resolve, reject) => {
428
- const shell =
429
- process.platform === "win32"
430
- ? process.env.ComSpec || "cmd.exe"
431
- : "/bin/sh";
432
- const shellArgs =
433
- process.platform === "win32"
434
- ? ["/d", "/s", "/c", command]
435
- : ["-c", command];
433
+ let windowsJob;
436
434
  let pendingSignal;
437
435
  let dispatchSignal = signal => {
438
436
  pendingSignal ??= signal;
@@ -455,7 +453,11 @@ export function supervise(command, timeoutMs, reap = reapTree, options = {}) {
455
453
 
456
454
  const child = (() => {
457
455
  try {
458
- return spawn(shell, shellArgs, {
456
+ if (process.platform === "win32") {
457
+ windowsJob = startWindowsProcessJob(command);
458
+ return windowsJob.child;
459
+ }
460
+ return spawn("/bin/sh", ["-c", command], {
459
461
  detached: true,
460
462
  env: process.env,
461
463
  stdio: "inherit",
@@ -501,7 +503,7 @@ export function supervise(command, timeoutMs, reap = reapTree, options = {}) {
501
503
  // shell's close handler is reaping must join that operation instead of
502
504
  // starting a second kill sequence or taking the default signal action.
503
505
  const reapOnce = () => {
504
- reapPromise ??= reap(pid);
506
+ reapPromise ??= windowsJob ? windowsJob.reap() : reap(pid);
505
507
  return reapPromise;
506
508
  };
507
509
  const finish = async (code, signal) => {
@@ -0,0 +1,249 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+ // Windows 10 / Server 2016 or newer: create the shell inside its Job Object.
4
+ using System;
5
+ using System.ComponentModel;
6
+ using System.Diagnostics;
7
+ using System.IO;
8
+ using System.Runtime.InteropServices;
9
+ using System.Text;
10
+ using System.Threading;
11
+
12
+ /// <summary>Runs a shell inside a retained job and confirms its descendants have exited.</summary>
13
+ public static class LisaWindowsProcessJob
14
+ {
15
+ [StructLayout(LayoutKind.Sequential)]
16
+ struct BasicLimits
17
+ {
18
+ public long ProcessTime, JobTime;
19
+ public uint Flags;
20
+ public UIntPtr MinimumWorkingSet, MaximumWorkingSet;
21
+ public uint ActiveProcessLimit;
22
+ public UIntPtr Affinity;
23
+ public uint Priority, SchedulingClass;
24
+ }
25
+
26
+ [StructLayout(LayoutKind.Sequential)]
27
+ struct ExtendedLimits
28
+ {
29
+ public BasicLimits Basic;
30
+ public ulong ReadOperations, WriteOperations, OtherOperations;
31
+ public ulong ReadBytes, WriteBytes, OtherBytes;
32
+ public UIntPtr ProcessMemory, JobMemory, PeakProcessMemory, PeakJobMemory;
33
+ }
34
+
35
+ [StructLayout(LayoutKind.Sequential)]
36
+ struct Accounting
37
+ {
38
+ public long UserTime, KernelTime, PeriodUserTime, PeriodKernelTime;
39
+ public uint PageFaults, TotalProcesses, ActiveProcesses, TerminatedProcesses;
40
+ }
41
+
42
+ [StructLayout(LayoutKind.Sequential)]
43
+ struct StartupInfo
44
+ {
45
+ public uint Size;
46
+ public IntPtr Reserved, Desktop, Title;
47
+ public uint X, Y, Width, Height, XCount, YCount, Fill, Flags;
48
+ public ushort ShowWindow, ReservedLength;
49
+ public IntPtr ReservedData, Input, Output, Error;
50
+ }
51
+
52
+ [StructLayout(LayoutKind.Sequential)]
53
+ struct StartupInfoEx
54
+ {
55
+ public StartupInfo Startup;
56
+ public IntPtr Attributes;
57
+ }
58
+
59
+ [StructLayout(LayoutKind.Sequential)]
60
+ struct ProcessInformation
61
+ {
62
+ public IntPtr Process, Thread;
63
+ public uint ProcessId, ThreadId;
64
+ }
65
+
66
+ /// <summary>Creates the helper-owned job handle, which is never inherited by the shell.</summary>
67
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
68
+ static extern IntPtr CreateJobObjectW(IntPtr attributes, string name);
69
+ /// <summary>Applies the job limits used to kill members when its final handle closes.</summary>
70
+ [DllImport("kernel32.dll", SetLastError = true)]
71
+ static extern bool SetInformationJobObject(IntPtr job, int kind, ref ExtendedLimits value, uint size);
72
+ /// <summary>Reads job accounting so cleanup can confirm zero active processes.</summary>
73
+ [DllImport("kernel32.dll", SetLastError = true)]
74
+ static extern bool QueryInformationJobObject(IntPtr job, int kind, out Accounting value, uint size, IntPtr returned);
75
+ /// <summary>Requests termination of every process retained in the job.</summary>
76
+ [DllImport("kernel32.dll", SetLastError = true)]
77
+ static extern bool TerminateJobObject(IntPtr job, uint code);
78
+ /// <summary>Measures or initializes storage for the two process-creation attributes.</summary>
79
+ [DllImport("kernel32.dll", SetLastError = true)]
80
+ static extern bool InitializeProcThreadAttributeList(IntPtr list, int count, uint flags, ref IntPtr size);
81
+ /// <summary>Adds the job binding or restricted inherited-handle list before process creation.</summary>
82
+ [DllImport("kernel32.dll", SetLastError = true)]
83
+ static extern bool UpdateProcThreadAttribute(IntPtr list, uint flags, IntPtr attribute, IntPtr value, IntPtr size, IntPtr previous, IntPtr returned);
84
+ /// <summary>Releases initialized attribute-list contents before their backing memory is freed.</summary>
85
+ [DllImport("kernel32.dll")]
86
+ static extern void DeleteProcThreadAttributeList(IntPtr list);
87
+ /// <summary>Creates the shell with job membership and explicit standard handles already assigned.</summary>
88
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
89
+ static extern bool CreateProcessW(string application, StringBuilder command, IntPtr processAttributes, IntPtr threadAttributes, bool inheritHandles, uint flags, IntPtr environment, string directory, ref StartupInfoEx startup, out ProcessInformation process);
90
+ /// <summary>Polls the shell handle while allowing stop-file and owner-exit checks.</summary>
91
+ [DllImport("kernel32.dll", SetLastError = true)]
92
+ static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);
93
+ /// <summary>Reads the shell exit code after its process handle signals completion.</summary>
94
+ [DllImport("kernel32.dll", SetLastError = true)]
95
+ static extern bool GetExitCodeProcess(IntPtr process, out uint code);
96
+ /// <summary>Releases an owned native handle without closing borrowed caller handles.</summary>
97
+ [DllImport("kernel32.dll", SetLastError = true)]
98
+ static extern bool CloseHandle(IntPtr handle);
99
+ /// <summary>Supplies the current-process pseudo handle for standard-handle duplication.</summary>
100
+ [DllImport("kernel32.dll")]
101
+ static extern IntPtr GetCurrentProcess();
102
+ /// <summary>Reads a borrowed standard handle, which may be absent in a service process.</summary>
103
+ [DllImport("kernel32.dll", SetLastError = true)]
104
+ static extern IntPtr GetStdHandle(int kind);
105
+ /// <summary>Opens a temporary NUL device handle when a standard stream is absent.</summary>
106
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
107
+ static extern IntPtr CreateFileW(string name, uint access, uint share, IntPtr attributes, uint creation, uint flags, IntPtr template);
108
+ /// <summary>Creates an inheritable duplicate while retaining ownership of the original handle.</summary>
109
+ [DllImport("kernel32.dll", SetLastError = true)]
110
+ static extern bool DuplicateHandle(IntPtr sourceProcess, IntPtr source, IntPtr targetProcess, out IntPtr target, uint access, bool inherit, uint options);
111
+
112
+ /// <summary>Throws with the captured native error when an operation reports failure.</summary>
113
+ static void Check(bool ok, string operation)
114
+ {
115
+ if (!ok) throw new Win32Exception(Marshal.GetLastWin32Error(), operation);
116
+ }
117
+
118
+ /// <summary>Returns an owned inheritable standard handle, substituting NUL only when absent.</summary>
119
+ /// <param name="kind">The native standard-input, output, or error handle identifier.</param>
120
+ /// <returns>A duplicate that Run must close after the contained command finishes.</returns>
121
+ static IntPtr StandardHandle(int kind)
122
+ {
123
+ IntPtr source = GetStdHandle(kind);
124
+ bool absent = source == IntPtr.Zero || source == new IntPtr(-1);
125
+ if (absent)
126
+ {
127
+ // Services may have no standard handles. Supply EOF/discard devices
128
+ // while preserving any real redirected handles supplied by callers.
129
+ source = CreateFileW("NUL", kind == -10 ? 0x80000000u : 0x40000000u, 3, IntPtr.Zero, 3, 0, IntPtr.Zero);
130
+ Check(source != new IntPtr(-1), "Open absent gate standard handle");
131
+ }
132
+ try
133
+ {
134
+ IntPtr result;
135
+ Check(DuplicateHandle(GetCurrentProcess(), source, GetCurrentProcess(), out result, 0, true, 2), "Duplicate gate standard handle");
136
+ return result;
137
+ }
138
+ finally
139
+ {
140
+ if (absent) CloseHandle(source);
141
+ }
142
+ }
143
+
144
+ /// <summary>Terminates job members and waits up to five seconds for empty membership.</summary>
145
+ /// <param name="job">The retained job handle; this method does not close it.</param>
146
+ static void TerminateAndWait(IntPtr job)
147
+ {
148
+ Check(TerminateJobObject(job, 255), "Terminate gate job");
149
+ Stopwatch deadline = Stopwatch.StartNew();
150
+ for (;;)
151
+ {
152
+ Accounting state;
153
+ Check(QueryInformationJobObject(job, 1, out state, (uint)Marshal.SizeOf(typeof(Accounting)), IntPtr.Zero), "Read gate job membership");
154
+ if (state.ActiveProcesses == 0) return;
155
+ if (deadline.ElapsedMilliseconds >= 5000) throw new TimeoutException("Gate job still has processes after termination");
156
+ Thread.Sleep(25);
157
+ }
158
+ }
159
+
160
+ /// <summary>Runs a contained command and reaps its descendants before returning.</summary>
161
+ /// <remarks>
162
+ /// The helper owns the job outside its membership. A helper crash closes
163
+ /// its non-inherited job handle and kills the job as a backstop.
164
+ /// </remarks>
165
+ /// <param name="command">The shell command, forwarded without rewriting its arguments.</param>
166
+ /// <param name="controlDirectory">The caller-owned directory containing the stop marker.</param>
167
+ /// <param name="parent">The retained owner process whose exit requests cancellation.</param>
168
+ /// <returns>The shell exit code, or 255 when interrupted before normal completion.</returns>
169
+ public static int Run(string command, string controlDirectory, Process parent)
170
+ {
171
+ IntPtr job = IntPtr.Zero, attributes = IntPtr.Zero;
172
+ IntPtr jobValue = IntPtr.Zero, handleValues = IntPtr.Zero;
173
+ bool attributesInitialized = false;
174
+ IntPtr[] standard = new IntPtr[3];
175
+ ProcessInformation child = new ProcessInformation();
176
+ uint result = 255;
177
+ try
178
+ {
179
+ job = CreateJobObjectW(IntPtr.Zero, null);
180
+ Check(job != IntPtr.Zero, "Create gate job");
181
+ ExtendedLimits limits = new ExtendedLimits();
182
+ limits.Basic.Flags = 0x2000; // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
183
+ Check(SetInformationJobObject(job, 9, ref limits, (uint)Marshal.SizeOf(typeof(ExtendedLimits))), "Set gate job lifetime");
184
+
185
+ IntPtr size = IntPtr.Zero;
186
+ InitializeProcThreadAttributeList(IntPtr.Zero, 2, 0, ref size);
187
+ Check(size != IntPtr.Zero, "Measure gate process attributes");
188
+ attributes = Marshal.AllocHGlobal(size);
189
+ Check(InitializeProcThreadAttributeList(attributes, 2, 0, ref size), "Initialize gate process attributes");
190
+ attributesInitialized = true;
191
+ jobValue = Marshal.AllocHGlobal(IntPtr.Size);
192
+ Marshal.WriteIntPtr(jobValue, job);
193
+ // PROC_THREAD_ATTRIBUTE_JOB_LIST binds before the first instruction;
194
+ // suspended-create followed by assignment has an orphaning window.
195
+ Check(UpdateProcThreadAttribute(attributes, 0, new IntPtr(0x2000D), jobValue, new IntPtr(IntPtr.Size), IntPtr.Zero, IntPtr.Zero), "Bind gate to job at creation");
196
+
197
+ handleValues = Marshal.AllocHGlobal(3 * IntPtr.Size);
198
+ for (int i = 0; i < 3; ++i)
199
+ {
200
+ standard[i] = StandardHandle(-10 - i);
201
+ Marshal.WriteIntPtr(handleValues, i * IntPtr.Size, standard[i]);
202
+ }
203
+ Check(UpdateProcThreadAttribute(attributes, 0, new IntPtr(0x20002), handleValues, new IntPtr(3 * IntPtr.Size), IntPtr.Zero, IntPtr.Zero), "Limit inherited gate handles");
204
+ StartupInfoEx startup = new StartupInfoEx();
205
+ startup.Startup.Size = (uint)Marshal.SizeOf(typeof(StartupInfoEx));
206
+ startup.Startup.Flags = 0x100; // STARTF_USESTDHANDLES.
207
+ startup.Startup.Input = standard[0];
208
+ startup.Startup.Output = standard[1];
209
+ startup.Startup.Error = standard[2];
210
+ startup.Attributes = attributes;
211
+ string shell = Environment.GetEnvironmentVariable("ComSpec") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
212
+ StringBuilder line = new StringBuilder("\"" + shell + "\" /d /s /c \"" + command + "\"");
213
+ // Never launch a command after a stop or owner exit during startup.
214
+ if (File.Exists(Path.Combine(controlDirectory, "stop")) || parent.WaitForExit(0)) return 255;
215
+ Check(CreateProcessW(shell, line, IntPtr.Zero, IntPtr.Zero, true, 0x80000, IntPtr.Zero, Environment.CurrentDirectory, ref startup, out child), "Create contained gate command");
216
+ for (;;)
217
+ {
218
+ uint wait = WaitForSingleObject(child.Process, 25);
219
+ if (wait == 0)
220
+ {
221
+ Check(GetExitCodeProcess(child.Process, out result), "Read gate command exit");
222
+ break;
223
+ }
224
+ Check(wait == 258, "Wait for gate command");
225
+ if (File.Exists(Path.Combine(controlDirectory, "stop")) || parent.WaitForExit(0)) break;
226
+ }
227
+ }
228
+ finally
229
+ {
230
+ // Release our process handles before checking membership accounting.
231
+ if (child.Thread != IntPtr.Zero) CloseHandle(child.Thread);
232
+ if (child.Process != IntPtr.Zero) CloseHandle(child.Process);
233
+ try
234
+ {
235
+ if (job != IntPtr.Zero) TerminateAndWait(job);
236
+ }
237
+ finally
238
+ {
239
+ if (job != IntPtr.Zero) CloseHandle(job);
240
+ foreach (IntPtr handle in standard) if (handle != IntPtr.Zero) CloseHandle(handle);
241
+ if (attributesInitialized) DeleteProcThreadAttributeList(attributes);
242
+ if (attributes != IntPtr.Zero) Marshal.FreeHGlobal(attributes);
243
+ if (jobValue != IntPtr.Zero) Marshal.FreeHGlobal(jobValue);
244
+ if (handleValues != IntPtr.Zero) Marshal.FreeHGlobal(handleValues);
245
+ }
246
+ }
247
+ return unchecked((int)result);
248
+ }
249
+ }
@@ -0,0 +1,119 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+ // The Windows-only helper owns a Job Object until all of its processes exit.
4
+ import { spawn } from "node:child_process";
5
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import path from "node:path";
8
+ import { env } from "node:process";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ const CLEANUP_TIMEOUT_MS = 30000;
12
+
13
+ /**
14
+ * Launch the Windows shell in a native job and return its cleanup operation.
15
+ * @param {string} command Shell source to run.
16
+ * @returns {{child: import("node:child_process").ChildProcess, reap: () => Promise<void>}} Native process boundary.
17
+ */
18
+ export function startWindowsProcessJob(command) {
19
+ const directory = mkdtempSync(path.join(tmpdir(), "lisa-windows-job-"));
20
+ const powershell = path.join(
21
+ env.SystemRoot || "C:\\Windows",
22
+ "System32",
23
+ "WindowsPowerShell",
24
+ "v1.0",
25
+ "powershell.exe"
26
+ );
27
+ const script = fileURLToPath(
28
+ new URL("./windows-process-job.ps1", import.meta.url)
29
+ );
30
+ let child;
31
+ try {
32
+ child = spawn(
33
+ powershell,
34
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-File", script],
35
+ {
36
+ // The native job supplies isolation. Keep the helper attached so
37
+ // Windows PowerShell retains the caller's standard handles.
38
+ stdio: "inherit",
39
+ env: {
40
+ ...env,
41
+ TEMP: directory,
42
+ TMP: directory,
43
+ LISA_WINDOWS_PROCESS_JOB: JSON.stringify({
44
+ command,
45
+ directory,
46
+ parentPid: process.pid,
47
+ cwd: process.cwd(),
48
+ originalTemp: env.TEMP ?? null,
49
+ originalTmp: env.TMP ?? null,
50
+ }),
51
+ },
52
+ }
53
+ );
54
+ } catch (error) {
55
+ rmSync(directory, { recursive: true, force: true });
56
+ throw error;
57
+ }
58
+ let closed = false;
59
+ const completion = new Promise(resolve => {
60
+ child.once("close", () => {
61
+ closed = true;
62
+ resolve();
63
+ });
64
+ child.once("error", () => {
65
+ if (child.pid === undefined)
66
+ rmSync(directory, { recursive: true, force: true });
67
+ });
68
+ });
69
+ const reap = async () => {
70
+ let timer;
71
+ try {
72
+ if (!closed) writeFileSync(path.join(directory, "stop"), "stop");
73
+ await Promise.race([
74
+ completion,
75
+ new Promise((_, reject) => {
76
+ timer = setTimeout(() => {
77
+ // Killing the owner closes its job handle as a last resort, but
78
+ // cannot supply a completed wait: reject instead of claiming reap.
79
+ child.kill("SIGKILL");
80
+ reject(
81
+ new Error("Windows gate helper did not complete its cleanup wait")
82
+ );
83
+ }, CLEANUP_TIMEOUT_MS);
84
+ }),
85
+ ]);
86
+ if (readFileSync(path.join(directory, "reaped"), "utf8") !== "reaped") {
87
+ throw new Error("Windows gate helper did not confirm an empty job");
88
+ }
89
+ } catch (error) {
90
+ // A failed stop-file write must still close the job owner. Its native
91
+ // KILL_ON_JOB_CLOSE limit stops contained processes; the finally block
92
+ // waits for helper close before removing scratch. Keep this a failure:
93
+ // forced termination cannot provide the normal empty-job receipt.
94
+ if (!closed && !child.killed) child.kill("SIGKILL");
95
+ throw new Error(
96
+ `Windows gate cleanup was not verified (helper exit ${child.exitCode ?? child.signalCode ?? "pending"}): ${error.message}`
97
+ );
98
+ } finally {
99
+ clearTimeout(timer);
100
+ if (!closed) {
101
+ // Give a killed helper time to close before a CLI rejection exits Node.
102
+ // Still bound failure when the OS cannot stop the helper promptly.
103
+ await Promise.race([
104
+ completion,
105
+ new Promise(resolve => {
106
+ timer = setTimeout(resolve, 1000);
107
+ }),
108
+ ]);
109
+ clearTimeout(timer);
110
+ }
111
+ if (closed) rmSync(directory, { recursive: true, force: true });
112
+ else
113
+ child.once("close", () =>
114
+ rmSync(directory, { recursive: true, force: true })
115
+ );
116
+ }
117
+ };
118
+ return { child, reap };
119
+ }
@@ -0,0 +1,25 @@
1
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ # Do not edit directly — durable changes belong upstream in Lisa.
3
+ # Use the installed Windows PowerShell policy; never override execution policy.
4
+ $ErrorActionPreference = 'Stop'
5
+ $parent = $null
6
+ try {
7
+ $config = $env:LISA_WINDOWS_PROCESS_JOB | ConvertFrom-Json
8
+ Remove-Item Env:LISA_WINDOWS_PROCESS_JOB
9
+ $parent = [Diagnostics.Process]::GetProcessById([int]$config.parentPid)
10
+ $null = $parent.Handle
11
+ Add-Type -Path (Join-Path $PSScriptRoot 'windows-process-job.cs')
12
+ # Compiler scratch belongs to this helper; the gate retains its original env.
13
+ [Environment]::SetEnvironmentVariable('TEMP', $config.originalTemp)
14
+ [Environment]::SetEnvironmentVariable('TMP', $config.originalTmp)
15
+ [Environment]::CurrentDirectory = $config.cwd
16
+ $code = [LisaWindowsProcessJob]::Run($config.command, $config.directory, $parent)
17
+ # Only a completed native membership wait may authorize a successful reap.
18
+ [IO.File]::WriteAllText((Join-Path $config.directory 'reaped'), 'reaped')
19
+ exit $code
20
+ } catch {
21
+ [Console]::Error.WriteLine('Windows gate containment failed: ' + $_.Exception.Message)
22
+ exit 1
23
+ } finally {
24
+ if ($null -ne $parent) { $parent.Dispose() }
25
+ }
@@ -0,0 +1,25 @@
1
+ import type { TemplateCandidate } from "../health/template-inspection.js";
2
+ import type { StarterChange } from "./starter-sync-source.js";
3
+ /**
4
+ * Resolve canonical markers; markerless copy-contents uses whole-file ownership.
5
+ * @param candidates - Applicable copy-contents template sources.
6
+ * @returns Distinct governed opening markers.
7
+ */
8
+ export declare function starterBlockMarkers(candidates: readonly TemplateCandidate[]): Promise<readonly string[]>;
9
+ /**
10
+ * Apply only the blocks governed by the existing canonical template sources.
11
+ * @param change - Exact before/after starter file.
12
+ * @param current - Existing consumer bytes.
13
+ * @param markers - Canonical opening markers for this path.
14
+ * @returns Updated bytes, preserving all consumer content outside those blocks.
15
+ */
16
+ export declare function mergeStarterBlocks(change: StarterChange, current: Buffer | undefined, markers: readonly string[]): Promise<Buffer>;
17
+ /**
18
+ * Reuse Lisa's JSON strategies for paths governed by JSON merge semantics.
19
+ * @param change - Pinned starter JSON file.
20
+ * @param current - Existing consumer bytes.
21
+ * @param candidates - Applicable JSON strategies in canonical order.
22
+ * @returns Merged bytes; source removal preserves the consumer's JSON document.
23
+ */
24
+ export declare function mergeStarterJson(change: StarterChange, current: Buffer | undefined, candidates: readonly TemplateCandidate[]): Buffer | undefined;
25
+ //# sourceMappingURL=starter-sync-content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-sync-content.d.ts","sourceRoot":"","sources":["../../src/cli/starter-sync-content.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAQ1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAyD9D;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,SAAS,iBAAiB,EAAE,GACvC,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAW5B;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,OAAO,CAAC,MAAM,CAAC,CAejB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,EAAE,SAAS,iBAAiB,EAAE,GACvC,MAAM,GAAG,SAAS,CAiBpB"}
@@ -0,0 +1,106 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { findCopyContentsBlock, mergeCopyContents, } from "../strategies/copy-contents.js";
3
+ import { mergeTemplateJson } from "../strategies/merge.js";
4
+ import { TaggedMergeStrategy } from "../strategies/tagged-merge.js";
5
+ import { isJsonObject } from "../sync/json-path.js";
6
+ const BEGIN = "# BEGIN: AI GUARDRAILS";
7
+ const END = "# END: AI GUARDRAILS";
8
+ /**
9
+ * Decode managed text without silently replacing invalid bytes.
10
+ * @param bytes - File payload, if the path exists.
11
+ * @returns Strict UTF-8 content.
12
+ */
13
+ function text(bytes) {
14
+ return bytes === undefined
15
+ ? ""
16
+ : new TextDecoder("utf-8", { fatal: true }).decode(bytes);
17
+ }
18
+ /**
19
+ * Extract one complete marker pair without importing outside starter content.
20
+ * @param content - Flat starter or consumer file.
21
+ * @param begin - Canonical Lisa template's opening marker.
22
+ * @returns Normalized managed block, or undefined when absent.
23
+ */
24
+ function block(content, begin) {
25
+ const end = END + begin.slice(BEGIN.length);
26
+ const lines = content.split(/\r?\n/u);
27
+ const starts = lines.flatMap((line, index) => line === begin ? [index] : []);
28
+ const ends = lines.flatMap((line, index) => (line === end ? [index] : []));
29
+ if (starts.length === 0 && ends.length === 0)
30
+ return undefined;
31
+ if (starts.length !== 1 || ends.length !== 1 || starts[0] >= ends[0]) {
32
+ throw new Error("Incomplete or ambiguous starter managed block");
33
+ }
34
+ return lines.slice(starts[0], ends[0] + 1).join("\n");
35
+ }
36
+ /**
37
+ * Replace or remove a verified managed block while retaining outside bytes.
38
+ * @param current - Consumer content.
39
+ * @param begin - Canonical opening marker.
40
+ * @param replacement - New block, or undefined for removal.
41
+ * @returns Updated content.
42
+ */
43
+ function replaceBlock(current, begin, replacement) {
44
+ if (replacement !== undefined)
45
+ return mergeCopyContents(`${replacement}\n`, current);
46
+ const end = END + begin.slice(BEGIN.length);
47
+ const offsets = findCopyContentsBlock(current, begin, end);
48
+ return offsets === undefined
49
+ ? current
50
+ : current.slice(0, offsets.start) + current.slice(offsets.end);
51
+ }
52
+ /**
53
+ * Resolve canonical markers; markerless copy-contents uses whole-file ownership.
54
+ * @param candidates - Applicable copy-contents template sources.
55
+ * @returns Distinct governed opening markers.
56
+ */
57
+ export async function starterBlockMarkers(candidates) {
58
+ const canonical = await Promise.all(candidates.map(candidate => readFile(candidate.source, "utf8")));
59
+ return [
60
+ ...new Set(canonical.flatMap(source => source.split(/\r?\n/u).filter(line => line.startsWith(BEGIN)))),
61
+ ];
62
+ }
63
+ /**
64
+ * Apply only the blocks governed by the existing canonical template sources.
65
+ * @param change - Exact before/after starter file.
66
+ * @param current - Existing consumer bytes.
67
+ * @param markers - Canonical opening markers for this path.
68
+ * @returns Updated bytes, preserving all consumer content outside those blocks.
69
+ */
70
+ export async function mergeStarterBlocks(change, current, markers) {
71
+ const before = text(change.before?.bytes);
72
+ const after = text(change.after?.bytes);
73
+ const merged = markers.reduce((content, begin) => {
74
+ const previous = block(before, begin);
75
+ const next = block(after, begin);
76
+ const existing = block(content, begin);
77
+ if (existing === next || previous === next)
78
+ return content;
79
+ if (existing !== undefined && existing !== previous)
80
+ throw new Error(`Starter managed block conflicts with local changes: ${change.path}`);
81
+ return replaceBlock(content, begin, next);
82
+ }, text(current));
83
+ return Buffer.from(merged);
84
+ }
85
+ /**
86
+ * Reuse Lisa's JSON strategies for paths governed by JSON merge semantics.
87
+ * @param change - Pinned starter JSON file.
88
+ * @param current - Existing consumer bytes.
89
+ * @param candidates - Applicable JSON strategies in canonical order.
90
+ * @returns Merged bytes; source removal preserves the consumer's JSON document.
91
+ */
92
+ export function mergeStarterJson(change, current, candidates) {
93
+ if (change.after === undefined)
94
+ return current;
95
+ const source = JSON.parse(text(change.after.bytes));
96
+ const destination = JSON.parse(current === undefined ? "{}" : text(current));
97
+ if (!isJsonObject(source) || !isJsonObject(destination))
98
+ throw new Error(`Starter JSON merge requires objects: ${change.path}`);
99
+ const merged = candidates.reduce((result, candidate) => candidate.strategy === "tagged-merge"
100
+ ? new TaggedMergeStrategy().mergeJson(source, result)
101
+ : mergeTemplateJson(source, result), destination);
102
+ if (JSON.stringify(merged) === JSON.stringify(destination))
103
+ return current;
104
+ return Buffer.from(`${JSON.stringify(merged, null, 2)}\n`);
105
+ }
106
+ //# sourceMappingURL=starter-sync-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-sync-content.js","sourceRoot":"","sources":["../../src/cli/starter-sync-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EACL,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,MAAM,KAAK,GAAG,wBAAwB,CAAC;AACvC,MAAM,GAAG,GAAG,sBAAsB,CAAC;AAEnC;;;;GAIG;AACH,SAAS,IAAI,CAAC,KAA6B;IACzC,OAAO,KAAK,KAAK,SAAS;QACxB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,SAAS,KAAK,CAAC,OAAe,EAAE,KAAa;IAC3C,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC3C,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAC9B,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAE,IAAI,IAAI,CAAC,CAAC,CAAE,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CACnB,OAAe,EACf,KAAa,EACb,WAA+B;IAE/B,IAAI,WAAW,KAAK,SAAS;QAC3B,OAAO,iBAAiB,CAAC,GAAG,WAAW,IAAI,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3D,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAwC;IAExC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAChE,CAAC;IACF,OAAO;QACL,GAAG,IAAI,GAAG,CACR,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CACzB,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAC9D,CACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAqB,EACrB,OAA2B,EAC3B,OAA0B;IAE1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,OAAO,CAAC;QAC3D,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,QAAQ;YACjD,MAAM,IAAI,KAAK,CACb,uDAAuD,MAAM,CAAC,IAAI,EAAE,CACrE,CAAC;QACJ,OAAO,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAClB,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAqB,EACrB,OAA2B,EAC3B,UAAwC;IAExC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IAC/C,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAY,IAAI,CAAC,KAAK,CACrC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAC7C,CAAC;IACF,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAC9B,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CACpB,SAAS,CAAC,QAAQ,KAAK,cAAc;QACnC,CAAC,CAAC,IAAI,mBAAmB,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;QACrD,CAAC,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,WAAW,CACZ,CAAC;IACF,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3E,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { StarterTemplate } from "../core/project-config-starter.js";
2
+ import { type CaptureCommand } from "./starter-provenance.js";
3
+ /** Exact regular-file or link bytes at one immutable starter revision. */
4
+ export interface StarterFile {
5
+ readonly bytes: Buffer;
6
+ readonly mode: string;
7
+ }
8
+ /** A path changed between the recorded baseline and the pinned tracked head. */
9
+ export interface StarterChange {
10
+ readonly path: string;
11
+ readonly before?: StarterFile;
12
+ readonly after?: StarterFile;
13
+ }
14
+ /** A complete, read-only diff suitable for one independently tracked starter. */
15
+ export interface StarterChanges {
16
+ readonly sha: string;
17
+ readonly changes: readonly StarterChange[];
18
+ }
19
+ /**
20
+ * Keep Git paths relative without rejecting colon-scoped agent commands.
21
+ * @param value - Repository path or configured scope.
22
+ * @returns Validated path, with a scope's trailing slash removed.
23
+ */
24
+ export declare function starterRelativePath(value: string): string;
25
+ /**
26
+ * Diff the actual recorded tree against one pinned head, never a merge base.
27
+ * @param template - Independently tracked starter and optional path scopes.
28
+ * @param capture - Bounded read-only GitHub metadata/content runner.
29
+ * @param accepts - Applicable ownership filter, applied before reading contents.
30
+ * @returns Complete scoped changes and the exact revision they describe.
31
+ */
32
+ export declare function readStarterChanges(template: StarterTemplate, capture?: CaptureCommand, accepts?: (name: string) => boolean): Promise<StarterChanges>;
33
+ //# sourceMappingURL=starter-sync-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-sync-source.d.ts","sourceRoot":"","sources":["../../src/cli/starter-sync-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAEjC,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAoBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWzD;AAyFD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,eAAe,EACzB,OAAO,GAAE,cAA+B,EACxC,OAAO,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAoB,GAC9C,OAAO,CAAC,cAAc,CAAC,CA0CzB"}