@juangcarmona/lighthouse 0.10.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +2 -2
- package/src/create-dotnet.mjs +1 -27
- package/src/lighthouse.mjs +27 -34
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ Use `lighthouse reconcile` to preview missing Lighthouse-managed paths in an exi
|
|
|
16
16
|
|
|
17
17
|
Use `lighthouse upgrade` to preview a versioned foundation update. The command is dry-run by default, accepts `--scope harness`, `--scope sdlc` or `--scope all`, preserves customized projections and stops when ownership evidence is missing. Add `--apply` only after reviewing the plan, then run `lighthouse verify`.
|
|
18
18
|
|
|
19
|
+
Agent workflows are opt-in through the SDLC package installer. Initialization does not create an entire worker catalog. Upgrading an older adoption releases workflow ownership while preserving the existing files for explicit review and cleanup.
|
|
20
|
+
|
|
19
21
|
The harness package owns the baseline command and verification contract. The SDLC package owns lifecycle skills, commands, metacommands, routes and gates, and `lighthouse init` records its selected version in `.lighthouse/sdlc.json`. The UI package owns visual tokens, native recipes, `DESIGN.md` and its agent skill. Selecting `visual-theme` projects the guide and skill into the adopter and enables its deterministic guardrail. Stack profiles own their source templates, guidance and checks; the .NET creator is the packaged entry point for the selected backend profile.
|
|
20
22
|
|
|
21
23
|
ProductShape, ArcShape and OpenSpec keep their own files and commands. Add them through their supported integration routes when the project needs them, and keep their versions and managed paths visible in the project configuration. In an existing repository, run `lighthouse authorities --adopt-existing` to validate the current ProductShape and ArcShape surfaces without running either initializer; review the dry-run plan and add `--apply` only to record both exact versions in Lighthouse-owned configuration.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juangcarmona/lighthouse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lighthouse adoption and verification CLI",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@arcshape/cli": "0.1.6",
|
|
24
24
|
"@juangcarmona/lighthouse-ui": "0.2.0",
|
|
25
25
|
"@prodshape/cli": "0.23.0",
|
|
26
|
-
"@juangcarmona/lighthouse-sdlc": "0.
|
|
26
|
+
"@juangcarmona/lighthouse-sdlc": "0.13.0"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src/lighthouse.mjs",
|
package/src/create-dotnet.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
3
|
import { dirname, join, resolve } from 'node:path';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
import { createRequire } from 'node:module';
|
|
@@ -10,23 +9,6 @@ const templateRoot = resolve(packageRoot, 'assets/dotnet-template');
|
|
|
10
9
|
const cliPackage = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8'));
|
|
11
10
|
const require = createRequire(import.meta.url);
|
|
12
11
|
const sdlcContract = JSON.parse(await readFile(require.resolve('@juangcarmona/lighthouse-sdlc/contract.json'), 'utf8'));
|
|
13
|
-
const sdlcPackageRoot = dirname(require.resolve('@juangcarmona/lighthouse-sdlc/contract.json'));
|
|
14
|
-
const sdlcWorkflowPaths = [
|
|
15
|
-
'.github/workflows/lighthouse-sdlc-router.yml',
|
|
16
|
-
'.github/workflows/lighthouse-sdlc-triage.md',
|
|
17
|
-
'.github/workflows/lighthouse-sdlc-triage.lock.yml',
|
|
18
|
-
'.github/workflows/lighthouse-sdlc-refine.md',
|
|
19
|
-
'.github/workflows/lighthouse-sdlc-refine.lock.yml',
|
|
20
|
-
'.github/workflows/lighthouse-sdlc-propose.md',
|
|
21
|
-
'.github/workflows/lighthouse-sdlc-propose.lock.yml',
|
|
22
|
-
'.github/workflows/lighthouse-sdlc-implement.md',
|
|
23
|
-
'.github/workflows/lighthouse-sdlc-implement.lock.yml',
|
|
24
|
-
'.github/workflows/lighthouse-sdlc-integrate.md',
|
|
25
|
-
'.github/workflows/lighthouse-sdlc-integrate.lock.yml',
|
|
26
|
-
'.github/workflows/lighthouse-sdlc-review.md',
|
|
27
|
-
'.github/workflows/lighthouse-sdlc-review.lock.yml',
|
|
28
|
-
'.github/workflows/shared/lighthouse-sdlc-stage.md',
|
|
29
|
-
];
|
|
30
12
|
const args = process.argv.slice(2);
|
|
31
13
|
const target = args[0] ? resolve(args[0]) : null;
|
|
32
14
|
const name = args.includes('--name') ? args[args.indexOf('--name') + 1] : null;
|
|
@@ -109,7 +91,7 @@ try {
|
|
|
109
91
|
product: name, application: { shape: 'backend', stacks: ['dotnet'] }, profiles: ['dotnet-backend'],
|
|
110
92
|
integrations: { productShape: null, arcShape: null },
|
|
111
93
|
packages: { sdlc: { name: '@juangcarmona/lighthouse-sdlc', version: sdlcContract.version } },
|
|
112
|
-
managed: ['LIGHTHOUSE.md', 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.lighthouse/sdlc.json',
|
|
94
|
+
managed: ['LIGHTHOUSE.md', 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.lighthouse/sdlc.json', '.agents/skills/lighthouse-init/SKILL.md', '.lighthouse/upgrade.json', '.lighthouse/commands.json', 'docs/profiles/dotnet/README.md', 'docs/profiles/dotnet/adoption.md', 'docs/profiles/dotnet/architecture.md', 'docs/profiles/dotnet/guardrails.md', 'docs/profiles/dotnet/practices.md', 'docs/profiles/dotnet/testing-and-development.md', 'docs/profiles/dotnet/use-cases.md', '.agents/skills/lighthouse-dotnet/SKILL.md'],
|
|
113
95
|
repository: {
|
|
114
96
|
layout: ['src', 'docs', 'infra', 'scripts'], publicSite: { enabled: true },
|
|
115
97
|
conventions: { commits: 'conventional', branchPattern: '^(feat|fix|docs|refactor|test|chore|build|ci|perf|revert)/[1-9]\\d*-[a-z0-9]+(?:-[a-z0-9]+)*$' },
|
|
@@ -126,14 +108,6 @@ try {
|
|
|
126
108
|
|
|
127
109
|
const initialized = (await import('node:child_process')).spawnSync(process.execPath, [resolve(stage, 'scripts/lighthouse.mjs'), 'init'], { cwd: stage, encoding: 'utf8' });
|
|
128
110
|
if (initialized.status !== 0) throw new Error(initialized.stderr || 'Projection generation failed.');
|
|
129
|
-
await copy(resolve(sdlcPackageRoot, 'workflows'), resolve(stage, '.github/workflows'));
|
|
130
|
-
const upgradePath = resolve(stage, '.lighthouse/upgrade.json');
|
|
131
|
-
const upgrade = JSON.parse(await readFile(upgradePath, 'utf8'));
|
|
132
|
-
for (const path of sdlcWorkflowPaths) {
|
|
133
|
-
const content = await readFile(resolve(stage, path), 'utf8');
|
|
134
|
-
upgrade.files[path] = { hash: createHash('sha256').update(content).digest('hex'), scope: 'sdlc' };
|
|
135
|
-
}
|
|
136
|
-
await writeFile(upgradePath, `${JSON.stringify(upgrade, null, 2)}\n`);
|
|
137
111
|
if (existing) await rmdir(target);
|
|
138
112
|
await rename(stage, target);
|
|
139
113
|
process.stdout.write(`Created ${name} in ${target}. Run dotnet restore ${name}.slnx, then npm run verify.\n`);
|
package/src/lighthouse.mjs
CHANGED
|
@@ -41,22 +41,6 @@ const upgradeStatePath = resolve(root, upgradeStateRelative);
|
|
|
41
41
|
const layout = ['src', 'docs', 'infra', 'scripts'];
|
|
42
42
|
const sdlcPackage = { name: '@juangcarmona/lighthouse-sdlc', version: installedPackageVersion('@juangcarmona/lighthouse-sdlc', 'contract.json') ?? '0.1.0' };
|
|
43
43
|
const uiPackage = { name: '@juangcarmona/lighthouse-ui', version: installedPackageVersion('@juangcarmona/lighthouse-ui', 'DESIGN.md') ?? '0.1.0' };
|
|
44
|
-
const sdlcWorkflowPaths = Object.freeze([
|
|
45
|
-
'.github/workflows/lighthouse-sdlc-router.yml',
|
|
46
|
-
'.github/workflows/lighthouse-sdlc-triage.md',
|
|
47
|
-
'.github/workflows/lighthouse-sdlc-triage.lock.yml',
|
|
48
|
-
'.github/workflows/lighthouse-sdlc-refine.md',
|
|
49
|
-
'.github/workflows/lighthouse-sdlc-refine.lock.yml',
|
|
50
|
-
'.github/workflows/lighthouse-sdlc-propose.md',
|
|
51
|
-
'.github/workflows/lighthouse-sdlc-propose.lock.yml',
|
|
52
|
-
'.github/workflows/lighthouse-sdlc-implement.md',
|
|
53
|
-
'.github/workflows/lighthouse-sdlc-implement.lock.yml',
|
|
54
|
-
'.github/workflows/lighthouse-sdlc-integrate.md',
|
|
55
|
-
'.github/workflows/lighthouse-sdlc-integrate.lock.yml',
|
|
56
|
-
'.github/workflows/lighthouse-sdlc-review.md',
|
|
57
|
-
'.github/workflows/lighthouse-sdlc-review.lock.yml',
|
|
58
|
-
'.github/workflows/shared/lighthouse-sdlc-stage.md',
|
|
59
|
-
]);
|
|
60
44
|
const managedPaths = [
|
|
61
45
|
projection,
|
|
62
46
|
'scripts/lighthouse-guard.mjs',
|
|
@@ -64,7 +48,6 @@ const managedPaths = [
|
|
|
64
48
|
'.husky/pre-push',
|
|
65
49
|
'.github/workflows/lighthouse.yml',
|
|
66
50
|
'.lighthouse/sdlc.json',
|
|
67
|
-
...sdlcWorkflowPaths,
|
|
68
51
|
'.agents/skills/lighthouse-init/SKILL.md',
|
|
69
52
|
upgradeStateRelative,
|
|
70
53
|
'.lighthouse/commands.json',
|
|
@@ -102,7 +85,7 @@ function managedPathsFor(manifest) {
|
|
|
102
85
|
function upgradeScopesFor(manifest) {
|
|
103
86
|
return {
|
|
104
87
|
harness: [projection, 'scripts/lighthouse-guard.mjs', '.husky/commit-msg', '.husky/pre-push', '.github/workflows/lighthouse.yml', '.agents/skills/lighthouse-init/SKILL.md', ...((manifest?.profiles ?? []).flatMap((profile) => profileManagedPaths[profile] ?? []))],
|
|
105
|
-
sdlc: ['.lighthouse/sdlc.json', '.lighthouse/commands.json'
|
|
88
|
+
sdlc: ['.lighthouse/sdlc.json', '.lighthouse/commands.json'],
|
|
106
89
|
};
|
|
107
90
|
}
|
|
108
91
|
|
|
@@ -111,18 +94,6 @@ function packageAsset(packageName, entry) {
|
|
|
111
94
|
return readFileSync(path, 'utf8');
|
|
112
95
|
}
|
|
113
96
|
|
|
114
|
-
function sdlcWorkflowFiles() {
|
|
115
|
-
const files = {};
|
|
116
|
-
for (const target of sdlcWorkflowPaths) {
|
|
117
|
-
try {
|
|
118
|
-
files[target] = packageAsset('@juangcarmona/lighthouse-sdlc', `workflows/${target.slice('.github/workflows/'.length)}`);
|
|
119
|
-
} catch {
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return files;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
97
|
function profileAssets(manifest) {
|
|
127
98
|
const assets = {};
|
|
128
99
|
if ((manifest?.profiles ?? []).includes('visual-theme')) {
|
|
@@ -306,11 +277,20 @@ function sdlcControlPlane(manifest) {
|
|
|
306
277
|
function render(manifest) {
|
|
307
278
|
const repository = normalizeRepository(manifest.repository);
|
|
308
279
|
const publicSite = repository.publicSite.enabled ? 'enabled' : 'disabled';
|
|
280
|
+
const authorities = [
|
|
281
|
+
manifest.integrations.productShape ? 'ProductShape is the adopted authority for product intent and Product Changes.' : 'ProductShape is not adopted; follow the repository-owned product intent and decision records.',
|
|
282
|
+
manifest.integrations.arcShape ? 'ArcShape is the adopted authority for architecture documentation and decisions.' : 'ArcShape is not adopted; follow the repository-owned architecture guidance.',
|
|
283
|
+
].join(' ');
|
|
284
|
+
const publicSiteContract = !repository.publicSite.enabled
|
|
285
|
+
? 'The Lighthouse documentation site is disabled.'
|
|
286
|
+
: Object.values(manifest.integrations).some(version => version === null)
|
|
287
|
+
? 'The Lighthouse documentation site is enabled but unavailable until both authorities are adopted and validate successfully.'
|
|
288
|
+
: 'The Lighthouse documentation site is enabled; publication requires successful ProductShape and ArcShape validation.';
|
|
309
289
|
const selectedSdlc = normalizeSdlcPackage(manifest);
|
|
310
290
|
const sdlcLine = selectedSdlc ? `- SDLC package: \`${selectedSdlc.name}@${selectedSdlc.version}\`\n` : '';
|
|
311
291
|
const selectedUi = manifest.packages?.ui;
|
|
312
292
|
const uiLine = selectedUi ? `- UI package: \`${selectedUi.name}@${selectedUi.version}\`\n` : '';
|
|
313
|
-
return `<!-- Generated by Lighthouse. Edit lighthouse.json and regenerate this file. -->\n\n# ${manifest.product} repository baseline\n\n- Baseline: \`${manifest.baseline}\`\n- Product: \`${manifest.product}\`\n- Profiles: ${manifest.profiles.length ? manifest.profiles.map((x) => `\`${x}\``).join(', ') : 'none'}\n${sdlcLine}${uiLine}- ProductShape: ${manifest.integrations.productShape ?? 'not adopted'}\n- ArcShape: ${manifest.integrations.arcShape ?? 'not adopted'}\n- Public site: \`${publicSite}\`\n- Repository roots: ${repository.layout.map((x) => `\`${x}/\``).join(', ')}\n- Commit convention: \`${repository.conventions.commits}\`\n\n## Delivery contract\n\
|
|
293
|
+
return `<!-- Generated by Lighthouse. Edit lighthouse.json and regenerate this file. -->\n\n# ${manifest.product} repository baseline\n\n- Baseline: \`${manifest.baseline}\`\n- Product: \`${manifest.product}\`\n- Profiles: ${manifest.profiles.length ? manifest.profiles.map((x) => `\`${x}\``).join(', ') : 'none'}\n${sdlcLine}${uiLine}- ProductShape: ${manifest.integrations.productShape ?? 'not adopted'}\n- ArcShape: ${manifest.integrations.arcShape ?? 'not adopted'}\n- Public site: \`${publicSite}\`\n- Repository roots: ${repository.layout.map((x) => `\`${x}/\``).join(', ')}\n- Commit convention: \`${repository.conventions.commits}\`\n\n## Delivery contract\n\n${authorities} Lighthouse installs the repository baseline and verifies the selected integrations.\n\nThe repository keeps source code in \`src/\`, documentation in \`docs/\`, infrastructure in \`infra/\` and repository automation in \`scripts/\`. A project may record a narrow, owned exception, but an initializer never overwrites an existing project file.\n\nThe selected SDLC package owns lifecycle skills, commands, meta-commands, routes and gates. The \`.lighthouse/\` control plane records the selected package, command aliases and host adapters without storing agent runtime data. Use \`lighthouse aliases --format json\` or \`.lighthouse/commands.json\` to discover canonical and short agent commands.\n\n${publicSiteContract} This route publishes product and architecture documentation. Application deployment follows the repository's own delivery policy.\n\nThis file is managed. Put repository-specific policy in its own documentation and keep this projection generated from \`lighthouse.json\`.\n`;
|
|
314
294
|
}
|
|
315
295
|
|
|
316
296
|
function guardrailScript(manifest) {
|
|
@@ -457,7 +437,6 @@ function managedFiles(manifest) {
|
|
|
457
437
|
'.agents/skills/lighthouse-init/SKILL.md': readFileSync(new URL('../assets/skills/init/SKILL.md', import.meta.url), 'utf8'),
|
|
458
438
|
};
|
|
459
439
|
Object.assign(files, profileAssets(manifest));
|
|
460
|
-
Object.assign(files, sdlcWorkflowFiles());
|
|
461
440
|
const controlPlane = sdlcControlPlane(manifest);
|
|
462
441
|
if (controlPlane) files['.lighthouse/sdlc.json'] = controlPlane;
|
|
463
442
|
files['.lighthouse/commands.json'] = commandAliasProjectionText(commandAliasesProjection());
|
|
@@ -542,6 +521,10 @@ function classifyUpgradeFile(path, current, expected, recordedHash) {
|
|
|
542
521
|
return { classification: 'conflicting', action: 'review', reason: 'ownership is not recorded for this path' };
|
|
543
522
|
}
|
|
544
523
|
|
|
524
|
+
function isOptionalWorkflow(path) {
|
|
525
|
+
return /^\.github\/workflows\/lighthouse-sdlc-(router\.yml|(?:triage|refine|propose|implement|integrate|review)\.(?:md|lock\.yml))$/.test(path) || path === '.github/workflows/shared/lighthouse-sdlc-stage.md';
|
|
526
|
+
}
|
|
527
|
+
|
|
545
528
|
async function planUpgrade(manifest, scopes) {
|
|
546
529
|
const state = await readUpgradeState();
|
|
547
530
|
const problems = stateProblems(state);
|
|
@@ -557,6 +540,9 @@ async function planUpgrade(manifest, scopes) {
|
|
|
557
540
|
const result = classifyUpgradeFile(path, current, expected, recordedHash);
|
|
558
541
|
changes.push({ path, scope: Object.entries(upgradeScopes).find(([, values]) => values.includes(path))?.[0], ...result });
|
|
559
542
|
}
|
|
543
|
+
if (scopes.has('sdlc') && manifest.managed.some(isOptionalWorkflow)) {
|
|
544
|
+
changes.push({ path: 'lighthouse.json', scope: 'sdlc', classification: 'adoptable', action: 'update', reason: 'release optional workflow ownership; preserve all workflow files' });
|
|
545
|
+
}
|
|
560
546
|
if (state?.baseline && state.baseline !== manifest.baseline) {
|
|
561
547
|
changes.unshift({ path: 'baseline', scope: 'harness', classification: 'breaking', action: 'review', reason: `baseline ${state.baseline} is not compatible with ${manifest.baseline}` });
|
|
562
548
|
}
|
|
@@ -688,7 +674,11 @@ function validate(manifest) {
|
|
|
688
674
|
const preSemanticWithoutUpgrade = Array.isArray(manifest.managed)
|
|
689
675
|
&& manifest.managed.length === preSemanticWithoutUpgradePaths.length
|
|
690
676
|
&& manifest.managed.every((entry, index) => entry === preSemanticWithoutUpgradePaths[index]);
|
|
691
|
-
|
|
677
|
+
// Earlier foundations installed every workflow. Accept that ownership inventory during upgrade;
|
|
678
|
+
// the foundation now leaves those files to explicit workflow adoption and never deletes them.
|
|
679
|
+
const withoutOldWorkflows = Array.isArray(manifest.managed) ? manifest.managed.filter(path => !isOptionalWorkflow(path)) : [];
|
|
680
|
+
const previousWorkflowManaged = withoutOldWorkflows.length === expectedManagedPaths.length && withoutOldWorkflows.every((path, index) => path === expectedManagedPaths[index]);
|
|
681
|
+
if (!previousWorkflowManaged && !legacyManaged && !profilelessManaged && !previousCompleteManaged && !completeManaged && !preSemanticManaged && !preSemanticWithoutUpgrade) {
|
|
692
682
|
problems.push(`managed must be ${legacyManagedPaths.join(', ')}, the previous Lighthouse guardrail set or the selected profile set`);
|
|
693
683
|
}
|
|
694
684
|
if ('repository' in manifest) {
|
|
@@ -990,7 +980,10 @@ async function applyUpgrade(manifest, plan) {
|
|
|
990
980
|
for (const change of updates) {
|
|
991
981
|
const target = resolve(root, change.path);
|
|
992
982
|
await mkdir(dirname(target), { recursive: true });
|
|
993
|
-
|
|
983
|
+
const content = change.path === 'lighthouse.json'
|
|
984
|
+
? `${JSON.stringify({ ...manifest, managed: manifest.managed.filter(path => !isOptionalWorkflow(path)) }, null, 2)}\n`
|
|
985
|
+
: files[change.path];
|
|
986
|
+
await writeTextAtomically(target, content);
|
|
994
987
|
}
|
|
995
988
|
const next = upgradeState(manifest, files, { ...previous, lastAppliedMigration: `upgrade-${cliPackage.version}` });
|
|
996
989
|
for (const path of Object.keys(next.files)) {
|