@msn-control/liftoff 0.3.3 → 0.4.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 (68) hide show
  1. package/README.md +66 -42
  2. package/assets/locks/frontend/package-lock.json +2217 -0
  3. package/assets/locks/frontend/package.json +19 -0
  4. package/assets/locks/node-backend/package-lock.json +4352 -0
  5. package/assets/locks/node-backend/package.json +32 -0
  6. package/dist/args.d.ts +9 -1
  7. package/dist/args.js +113 -33
  8. package/dist/args.js.map +1 -1
  9. package/dist/catalogs.d.ts +10 -1
  10. package/dist/catalogs.js +89 -0
  11. package/dist/catalogs.js.map +1 -1
  12. package/dist/cli.js +5 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands.d.ts +5 -0
  15. package/dist/commands.js +633 -124
  16. package/dist/commands.js.map +1 -1
  17. package/dist/file-system.js +89 -5
  18. package/dist/file-system.js.map +1 -1
  19. package/dist/framework-adapters.d.ts +15 -0
  20. package/dist/framework-adapters.js +112 -0
  21. package/dist/framework-adapters.js.map +1 -0
  22. package/dist/framework-validation.d.ts +8 -0
  23. package/dist/framework-validation.js +83 -0
  24. package/dist/framework-validation.js.map +1 -0
  25. package/dist/init-filesystem.d.ts +102 -0
  26. package/dist/init-filesystem.js +762 -0
  27. package/dist/init-filesystem.js.map +1 -0
  28. package/dist/interactive.d.ts +5 -1
  29. package/dist/interactive.js +75 -4
  30. package/dist/interactive.js.map +1 -1
  31. package/dist/npm-template-assets.d.ts +3 -0
  32. package/dist/npm-template-assets.js +32 -0
  33. package/dist/npm-template-assets.js.map +1 -0
  34. package/dist/planner.js +62 -3
  35. package/dist/planner.js.map +1 -1
  36. package/dist/process-runner.d.ts +28 -0
  37. package/dist/process-runner.js +80 -0
  38. package/dist/process-runner.js.map +1 -0
  39. package/dist/project-dependencies.d.ts +28 -0
  40. package/dist/project-dependencies.js +163 -0
  41. package/dist/project-dependencies.js.map +1 -0
  42. package/dist/published-verifier.d.ts +39 -0
  43. package/dist/published-verifier.js +173 -0
  44. package/dist/published-verifier.js.map +1 -0
  45. package/dist/reconcile.js +4 -1
  46. package/dist/reconcile.js.map +1 -1
  47. package/dist/release-identity.d.ts +17 -0
  48. package/dist/release-identity.js +51 -0
  49. package/dist/release-identity.js.map +1 -0
  50. package/dist/runtime.d.ts +2 -0
  51. package/dist/runtime.js +9 -0
  52. package/dist/runtime.js.map +1 -0
  53. package/dist/standard-templates.js +3 -30
  54. package/dist/standard-templates.js.map +1 -1
  55. package/dist/templates.d.ts +9 -1
  56. package/dist/templates.js +110 -46
  57. package/dist/templates.js.map +1 -1
  58. package/dist/terminal.d.ts +32 -0
  59. package/dist/terminal.js +182 -0
  60. package/dist/terminal.js.map +1 -0
  61. package/dist/types.d.ts +38 -1
  62. package/dist/workstation-catalog.d.ts +20 -0
  63. package/dist/workstation-catalog.js +122 -0
  64. package/dist/workstation-catalog.js.map +1 -0
  65. package/dist/workstation.d.ts +76 -0
  66. package/dist/workstation.js +461 -0
  67. package/dist/workstation.js.map +1 -0
  68. package/package.json +10 -2
@@ -0,0 +1,83 @@
1
+ import { lstat, readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getFrameworkDefinition } from './catalogs.js';
4
+ function errorCode(error) {
5
+ return typeof error === 'object' && error !== null && 'code' in error &&
6
+ typeof error.code === 'string'
7
+ ? error.code
8
+ : undefined;
9
+ }
10
+ async function markerIssue(root, pathParts) {
11
+ const display = pathParts.join('/');
12
+ try {
13
+ const details = await lstat(path.join(root, ...pathParts));
14
+ if (details.isSymbolicLink()) {
15
+ return `Framework marker is a forbidden symlink: ${display}`;
16
+ }
17
+ if (!details.isFile()) {
18
+ return `Framework marker is not a regular file: ${display}`;
19
+ }
20
+ return undefined;
21
+ }
22
+ catch (error) {
23
+ if (errorCode(error) === 'ENOENT') {
24
+ return `Missing framework marker: ${display}`;
25
+ }
26
+ return `Unable to inspect framework marker ${display}: ${error instanceof Error ? error.message : String(error)}`;
27
+ }
28
+ }
29
+ async function validateSpecKitState(root, selection) {
30
+ const issues = [];
31
+ let parsed;
32
+ try {
33
+ parsed = JSON.parse(await readFile(path.join(root, '.specify', 'integration.json'), 'utf8'));
34
+ }
35
+ catch (error) {
36
+ return [`Unable to read .specify/integration.json: ${error instanceof Error ? error.message : String(error)}`];
37
+ }
38
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
39
+ return ['Spec Kit integration state must be a JSON object.'];
40
+ }
41
+ const state = parsed;
42
+ const expectedDefault = selection.defaultAgent === 'github-copilot' ? 'copilot' : selection.defaultAgent;
43
+ const defaultIntegration = state.default_integration ?? state.integration;
44
+ if (defaultIntegration !== expectedDefault) {
45
+ issues.push(`Spec Kit default integration is ${JSON.stringify(defaultIntegration)}; expected ${JSON.stringify(expectedDefault)}.`);
46
+ }
47
+ const installed = state.installed_integrations;
48
+ if (!Array.isArray(installed) || installed.some((value) => typeof value !== 'string')) {
49
+ issues.push('Spec Kit installed_integrations must be a string array.');
50
+ return issues;
51
+ }
52
+ const expected = selection.agents.map((agent) => agent === 'github-copilot' ? 'copilot' : agent);
53
+ for (const integration of expected) {
54
+ if (!installed.includes(integration)) {
55
+ issues.push(`Spec Kit integration state does not include selected integration ${integration}.`);
56
+ }
57
+ }
58
+ return issues;
59
+ }
60
+ export async function validateFrameworkInstallation(root, selection) {
61
+ const definition = getFrameworkDefinition(selection.workflow);
62
+ const markers = [
63
+ ...definition.baseMarkers,
64
+ ...selection.agents.flatMap((agent) => definition.agentMarkers[agent])
65
+ ];
66
+ const issues = (await Promise.all(markers.map((marker) => markerIssue(root, marker))))
67
+ .filter((issue) => issue !== undefined);
68
+ if (selection.workflow === 'spec-kit' && !selection.defaultAgent) {
69
+ issues.push('Spec Kit framework state is missing its default agent.');
70
+ }
71
+ else if (selection.workflow === 'spec-kit') {
72
+ issues.push(...await validateSpecKitState(root, selection));
73
+ }
74
+ return issues;
75
+ }
76
+ export function frameworkSelectionFromPlan(plan) {
77
+ return {
78
+ workflow: plan.specWorkflow.id,
79
+ agents: plan.agents.map((agent) => agent.id),
80
+ ...(plan.defaultAgent ? { defaultAgent: plan.defaultAgent.id } : {})
81
+ };
82
+ }
83
+ //# sourceMappingURL=framework-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-validation.js","sourceRoot":"","sources":["../src/framework-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import type { CommandRunner } from './process-runner.js';
2
+ import type { GeneratedArtifact } from './types.js';
3
+ export type InitTargetMode = 'in-place' | 'named-child';
4
+ export type StagedOrigin = 'liftoff' | 'framework' | 'seed';
5
+ export type TreeEntryType = 'file' | 'directory' | 'symlink' | 'other';
6
+ export type PreflightAction = 'create' | 'identical' | 'replace' | 'merge-directory' | 'blocked';
7
+ export interface GitRootDiscovery {
8
+ cwd: string;
9
+ canonicalCwd: string;
10
+ root?: string;
11
+ exact: boolean;
12
+ }
13
+ export interface InitTarget {
14
+ root: string;
15
+ mode: InitTargetMode;
16
+ gitRoot?: string;
17
+ }
18
+ export interface StagingArea {
19
+ root: string;
20
+ origins: Map<string, StagedOrigin>;
21
+ frameworkAllowedRoots: Set<string>;
22
+ }
23
+ export interface TreeStateEntry {
24
+ pathParts: string[];
25
+ type: TreeEntryType;
26
+ contentHash?: string;
27
+ mode?: number;
28
+ }
29
+ export interface ValidatedStagedFile {
30
+ pathParts: string[];
31
+ relativePath: string;
32
+ content: Buffer;
33
+ contentHash: string;
34
+ mode: number;
35
+ origin: StagedOrigin;
36
+ }
37
+ export interface TargetRootSnapshot {
38
+ state: 'missing' | 'directory';
39
+ canonicalPath: string;
40
+ parentCanonicalPath: string;
41
+ parentDevice: number;
42
+ parentInode: number;
43
+ device?: number;
44
+ inode?: number;
45
+ }
46
+ export interface PreflightEntry {
47
+ pathParts: readonly string[];
48
+ relativePath: string;
49
+ stagedType: 'file' | 'directory';
50
+ origin?: StagedOrigin;
51
+ action: PreflightAction;
52
+ detail: string;
53
+ stagedHash?: string;
54
+ stagedMode?: number;
55
+ destination: {
56
+ type: 'missing' | TreeEntryType;
57
+ contentHash?: string;
58
+ mode?: number;
59
+ };
60
+ }
61
+ export interface MergePreflight {
62
+ stagingRoot: string;
63
+ targetRoot: string;
64
+ targetRootSnapshot: TargetRootSnapshot;
65
+ entries: readonly PreflightEntry[];
66
+ replacements: readonly PreflightEntry[];
67
+ blocked: readonly PreflightEntry[];
68
+ }
69
+ export interface MergeResult {
70
+ created: string[];
71
+ replaced: string[];
72
+ identical: string[];
73
+ mergedDirectories: string[];
74
+ }
75
+ export interface RollbackReport {
76
+ restored: string[];
77
+ removed: string[];
78
+ failures: string[];
79
+ }
80
+ export declare class InitFileSystemError extends Error {
81
+ constructor(message: string);
82
+ }
83
+ export declare class MergeApplyError extends InitFileSystemError {
84
+ readonly rollback: RollbackReport;
85
+ constructor(message: string, rollback: RollbackReport);
86
+ }
87
+ export declare function normalizeComparisonPath(value: string, platform?: NodeJS.Platform): string;
88
+ export declare function discoverGitRoot(cwd: string, runner: CommandRunner, platform?: NodeJS.Platform): Promise<GitRootDiscovery>;
89
+ export declare function resolveInitTarget(cwd: string, safeProjectName: string, runner: CommandRunner, platform?: NodeJS.Platform): Promise<InitTarget>;
90
+ export declare function resolveInitTargetFromDiscovery(git: GitRootDiscovery, safeProjectName: string): InitTarget;
91
+ export declare function assertSafeInitTarget(target: InitTarget, confinementRoot?: string): Promise<void>;
92
+ export declare function withStagingArea<T>(operation: (area: StagingArea) => Promise<T>): Promise<T>;
93
+ export declare function writeStagedArtifacts(area: StagingArea, artifacts: GeneratedArtifact[], origin: StagedOrigin): Promise<void>;
94
+ export declare function captureTreeState(root: string): Promise<Map<string, TreeStateEntry>>;
95
+ export declare function claimFrameworkChanges(area: StagingArea, before: Map<string, TreeStateEntry>, allowedRoots: string[]): Promise<string[]>;
96
+ export declare function validateStagedTree(area: StagingArea): Promise<ValidatedStagedFile[]>;
97
+ export declare function buildMergePreflight(area: StagingArea, targetRoot: string): Promise<MergePreflight>;
98
+ export declare function authorizeMergePreflight(preflight: MergePreflight, force: boolean, confirm?: (replacementPaths: readonly string[]) => Promise<boolean>): Promise<MergePreflight | undefined>;
99
+ export declare function applyMergePreflight(preflight: MergePreflight, options?: {
100
+ onBeforeMutation?: (entry: PreflightEntry, index: number) => Promise<void>;
101
+ requireEmptyTarget?: boolean;
102
+ }): Promise<MergeResult>;