* chore: rename npm package + bin to @opengsd/gsd-core (functional) - package.json: name @opengsd/get-shit-done-redux → @opengsd/gsd-core, bin key get-shit-done-redux → gsd-core, repository/homepage/bugs URLs - package-lock.json: regenerated (npm install --package-lock-only) - tests/**, scripts/**, bin/**, .github/**, agents/**, commands/**, get-shit-done/bin/**, get-shit-done/workflows/**: applied the 4-rule replacement (scoped npm ref, GitHub repo path, bin/clone invocations) per #505 single-source refactor Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: sweep live references to @opengsd/gsd-core Update all live documentation (README.md + translations, docs/**, CONTRIBUTING.md, VERSIONING.md, SECURITY.md, CONTEXT.md, docs/CANARY.md) to reflect the renamed package and repository. Rules applied: - @opengsd/get-shit-done-redux → @opengsd/gsd-core (scoped npm name) - open-gsd/get-shit-done-redux → open-gsd/gsd-core (GitHub repo) - GSD-redux/get-shit-done-redux → open-gsd/gsd-core (stale badge org) - bare bin/clone refs → gsd-core CHANGELOG.md, docs/adr/**, docs/RELEASE-*.md, docs/research/**, and .changeset/** are preserved byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: add negative lookbehind to slash-command regex in bug-2954 test The extractSlashReferences regex matched /gsd-core inside npm package URLs (@opengsd/gsd-core), producing a false /gsd:core command reference. Adding a negative lookbehind (?<![a-z]) excludes matches preceded by a letter, so only standalone /gsd-<cmd> and /gsd:<cmd> tokens are found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#518): add changeset for package rename Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#518): update package-identity expectations to the renamed coordinates The rebase regenerated the seam to @opengsd/gsd-core (bin gsd-core, repo open-gsd/gsd-core). The #498 seam tests assert deriveIdentity against the REAL package.json, so their expected literals must follow the rename. The drift-lint unit test is left as-is — its SEAM is a self-consistent fixture and its stale-literal detection cases would shift if altered; the live-repo scan in it already passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
116 lines
3.9 KiB
JavaScript
116 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Lint guard: the package name must be single-sourced from package-identity.cjs.
|
|
*
|
|
* Scans runtime files (bin/install.js, get-shit-done/bin/**, scripts/*.cjs)
|
|
* and FAILS if the literal `@opengsd/gsd-core` appears in a
|
|
* non-comment, non-identity-module line. This enforces that a future rename
|
|
* is a one-file change in package.json (#516).
|
|
*
|
|
* Comment lines are detected by checking if the trimmed line starts with
|
|
* `//`, `*`, or `/*` (best-effort; covers all common JS comment forms).
|
|
*
|
|
* Tests:
|
|
* 1. No code-literal occurrences of the package name in runtime files
|
|
* (outside the identity module itself).
|
|
* 2. PACKAGE_NAME exported from the identity module equals package.json name.
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const { execSync } = require('node:child_process');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const LITERAL = '@opengsd/gsd-core';
|
|
const IDENTITY_MODULE = path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'package-identity.cjs');
|
|
|
|
// Files to scan: bin/install.js + everything under get-shit-done/bin/ + touched scripts
|
|
function getRuntimeFiles() {
|
|
const files = [];
|
|
|
|
// bin/install.js
|
|
const installJs = path.join(ROOT, 'bin', 'install.js');
|
|
if (fs.existsSync(installJs)) files.push(installJs);
|
|
|
|
// get-shit-done/bin/**/*.cjs and *.js (recursive)
|
|
function collectDir(dir) {
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
collectDir(fullPath);
|
|
} else if (entry.isFile() && /\.(cjs|js)$/.test(entry.name)) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
}
|
|
collectDir(path.join(ROOT, 'get-shit-done', 'bin'));
|
|
|
|
// scripts/*.cjs
|
|
for (const entry of fs.readdirSync(path.join(ROOT, 'scripts'), { withFileTypes: true })) {
|
|
if (entry.isFile() && entry.name.endsWith('.cjs')) {
|
|
files.push(path.join(ROOT, 'scripts', entry.name));
|
|
}
|
|
}
|
|
// scripts/changeset/*.cjs
|
|
const changesetDir = path.join(ROOT, 'scripts', 'changeset');
|
|
if (fs.existsSync(changesetDir)) {
|
|
for (const entry of fs.readdirSync(changesetDir, { withFileTypes: true })) {
|
|
if (entry.isFile() && entry.name.endsWith('.cjs')) {
|
|
files.push(path.join(changesetDir, entry.name));
|
|
}
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
function isCommentLine(trimmed) {
|
|
return (
|
|
trimmed.startsWith('//') ||
|
|
trimmed.startsWith('*') ||
|
|
trimmed.startsWith('/*')
|
|
);
|
|
}
|
|
|
|
test('no hardcoded @opengsd/gsd-core literals in runtime non-comment code lines (#516)', () => {
|
|
const files = getRuntimeFiles();
|
|
const violations = [];
|
|
|
|
for (const file of files) {
|
|
// Skip the identity module itself — it legitimately contains the literal
|
|
if (path.resolve(file) === path.resolve(IDENTITY_MODULE)) continue;
|
|
|
|
const content = fs.readFileSync(file, 'utf-8');
|
|
const lines = content.split('\n');
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
if (!line.includes(LITERAL)) continue;
|
|
const trimmed = line.trimStart();
|
|
if (isCommentLine(trimmed)) continue;
|
|
violations.push(`${path.relative(ROOT, file)}:${i + 1}: ${line.trim()}`);
|
|
}
|
|
}
|
|
|
|
assert.deepEqual(
|
|
violations,
|
|
[],
|
|
`Found ${violations.length} hardcoded @opengsd/gsd-core literal(s) in non-comment code lines:\n` +
|
|
violations.map(v => ` ${v}`).join('\n') +
|
|
'\n\nReplace each with the PACKAGE_NAME imported from get-shit-done/bin/lib/package-identity.cjs'
|
|
);
|
|
});
|
|
|
|
test('PACKAGE_NAME from identity module matches package.json name (#516)', () => {
|
|
const { PACKAGE_NAME } = require('../get-shit-done/bin/lib/package-identity.cjs');
|
|
const pkgJson = require('../package.json');
|
|
assert.equal(
|
|
PACKAGE_NAME,
|
|
pkgJson.name,
|
|
`Identity module PACKAGE_NAME (${PACKAGE_NAME}) must equal package.json name (${pkgJson.name})`
|
|
);
|
|
});
|