Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .configs/.prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
"options": {
"proseWrap": "preserve"
}
},
{
"files": "*.jsonv",
"options": {
"parser": "jsonv"
}
}
]
],
"plugins": ["@cldmv/prettier-plugin-jsonv"]
}
28 changes: 25 additions & 3 deletions .configs/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import js from "@eslint/js";
import globals from "globals";
import json from "@eslint/json";
import jsonvPlugin from "@cldmv/eslint-plugin-jsonv";
import markdown from "@eslint/markdown";
import css from "@eslint/css";
import { defineConfig } from "eslint/config";

export default defineConfig([
{
ignores: [
"tmp/**",
"trash/**",
"node_modules/**",
"dist/**",
"build/**",
Expand All @@ -15,7 +19,21 @@ export default defineConfig([
".vscode/**",
"coverage/**",
"reference/**",
"**/package-lock.json"
"*.min.*",
"**/package-lock.json",
// Copy file patterns
"*copy/",
"*copy (*)/",
"*copy */",
"*copy.*",
"*copy (*).*",
"*copy *.*",
"**/*copy/",
"**/*copy (*)/",
"**/*copy */",
"**/*copy.*",
"**/*copy (*).*",
"**/*copy *.*"
]
},
{
Expand All @@ -35,7 +53,7 @@ export default defineConfig([
}
},
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
{ files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: { ...globals.node } } },
{ files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: { ...globals.node, ...globals.browser } } },
{
files: ["tests/**/*.test.vitest.mjs"],
languageOptions: {
Expand All @@ -54,13 +72,17 @@ export default defineConfig([
},
{ files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] },
{ files: ["**/*.jsonc"], plugins: { json }, language: "json/jsonc", extends: ["json/recommended"] },
{ files: ["**/*.json5"], plugins: { json }, language: "json/json5", extends: ["json/recommended"] },
{ files: ["**/*.jsonv"], plugins: { jsonv: jsonvPlugin }, language: "jsonv/jsonv", ...jsonvPlugin.configs.recommended },
{
files: ["**/*.md"],
plugins: { markdown },
language: "markdown/gfm",
extends: ["markdown/recommended"],
rules: {
// GitHub alerts like [!NOTE]/[!WARNING] are valid but trip this rule.
"markdown/no-missing-label-refs": "off"
}
}
},
{ files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] }
]);
16 changes: 16 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Dependencies + generated/build output (not hand-authored)
node_modules/
dist/
build/
coverage/
tmp/
trash/
reference/
package-lock.json
# minified / compact generated files of any extension (*.min.js/.css/.json/.map…)
*.min.*

# YAML β€” CI workflows are deliberately 4-space indented; prettier (tabWidth 2,
# and YAML disallows tabs) would reindent every file 4->2 for no real gain.
*.yml
*.yaml
4 changes: 2 additions & 2 deletions docs/use-case-private-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The classical clean-room reimplementation defense β€” used most famously by Phoe

The defense is strong when the dirty team is genuinely good at abstraction. It is weak when the "spec" they hand off is mostly the original code transliterated into prose β€” that intermediate spec is itself a derivative work, and so is the downstream output. Humans trained as engineers tend to be careful at this; large language models tend not to be, paraphrasing more than abstracting.

Comprehensive tests bypass the dirty team entirely. They are already a behavioral specification β€” that's their entire purpose β€” and they were written to be implementation-independent. A test file is *not* a derivative work of the implementation in any meaningful sense; courts have held repeatedly that purely functional descriptions, API surfaces, and behavioral specifications are not protectable expression (Google v. Oracle 2021, Computer Associates v. Altai 1992, Sega v. Accolade 1992). The output produced by an AI given test files as input is not, on its face, substantially similar to the original code β€” especially if the reimplementation is in a different programming language.
Comprehensive tests bypass the dirty team entirely. They are already a behavioral specification β€” that's their entire purpose β€” and they were written to be implementation-independent. A test file is _not_ a derivative work of the implementation in any meaningful sense; courts have held repeatedly that purely functional descriptions, API surfaces, and behavioral specifications are not protectable expression (Google v. Oracle 2021, Computer Associates v. Altai 1992, Sega v. Accolade 1992). The output produced by an AI given test files as input is not, on its face, substantially similar to the original code β€” especially if the reimplementation is in a different programming language.

The threat is therefore:

Expand Down Expand Up @@ -45,7 +45,7 @@ Several alternatives were considered for tracking the pinned tests version:
- **A submodule with a fake URL** in `.gitmodules`, overridden locally via `.git/config`. Git automation works because the registry entry exists; the URL leak is reduced because the public URL is a non-resolving decoy. But the decoy itself advertises "we are deliberately hiding the real URL," which signals more intent than no entry at all.
- **An anonymous gitlink** (gitlink in the tree with no `.gitmodules` entry). Most opaque β€” the public parent shows the gitlink and SHA but no URL, which could plausibly be a leftover, mistake, or any other innocuous explanation. Loses git's automation, which is the gap this package fills with its hooks.

The choice is partly aesthetic but mostly about how the parent repo presents itself to outside observers. The anonymous gitlink presents minimum signal: an embedded reference whose target is not declared, the way most other features in a repo work (you can see *that* something exists without being told everything about *what* it is).
The choice is partly aesthetic but mostly about how the parent repo presents itself to outside observers. The anonymous gitlink presents minimum signal: an embedded reference whose target is not declared, the way most other features in a repo work (you can see _that_ something exists without being told everything about _what_ it is).

## Licensing strategy

Expand Down
3 changes: 3 additions & 0 deletions messages/setup-dispatcher-missing-symlinks.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The CLI will then install `git-embedded`'s hook scripts into this repo's `.git/h
If you decline the heal, the CLI exits without changes. You can heal manually:

**Linux / macOS (symlinks, no elevation needed):**

```bash
ln -sf _dispatch <dispatcher-dir>/post-checkout
ln -sf _dispatch <dispatcher-dir>/post-merge
Expand All @@ -36,6 +37,7 @@ ln -sf _dispatch <dispatcher-dir>/reference-transaction
```

**Windows (symlinks, run from an elevated Command Prompt):**

```cmd
mklink "<dispatcher-dir>\post-checkout" "<dispatcher-dir>\_dispatch"
mklink "<dispatcher-dir>\post-merge" "<dispatcher-dir>\_dispatch"
Expand All @@ -44,6 +46,7 @@ mklink "<dispatcher-dir>\reference-transaction" "<dispatcher-dir>\_dispatch"
```

**Windows (hard links, no elevation needed β€” alternative if you don't want to elevate):**

```cmd
mklink /H "<dispatcher-dir>\post-checkout" "<dispatcher-dir>\_dispatch"
mklink /H "<dispatcher-dir>\post-merge" "<dispatcher-dir>\_dispatch"
Expand Down
5 changes: 3 additions & 2 deletions messages/setup-pre-commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ repos:
entry: npx git-embedded run-hook reference-transaction
language: system
pass_filenames: false
stages: [reference-transaction] # support for this stage depends on
# the pre-commit version
stages:
[reference-transaction] # support for this stage depends on
# the pre-commit version
verbose: true
```

Expand Down
12 changes: 6 additions & 6 deletions messages/setup-simple-git-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ Edit `package.json` to add `git-embedded`'s invocations to the `simple-git-hooks

```json
{
"simple-git-hooks": {
"post-checkout": "npx git-embedded run-hook post-checkout \"$@\"",
"post-merge": "npx git-embedded run-hook post-merge \"$@\"",
"post-rewrite": "npx git-embedded run-hook post-rewrite \"$@\"",
"reference-transaction": "npx git-embedded run-hook reference-transaction \"$@\""
}
"simple-git-hooks": {
"post-checkout": "npx git-embedded run-hook post-checkout \"$@\"",
"post-merge": "npx git-embedded run-hook post-merge \"$@\"",
"post-rewrite": "npx git-embedded run-hook post-rewrite \"$@\"",
"reference-transaction": "npx git-embedded run-hook reference-transaction \"$@\""
}
}
```

Expand Down
90 changes: 88 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cldmv/git-embedded",
"version": "1.1.6",
"version": "1.1.7",
"description": "Manage embedded git repositories (anonymous gitlinks) without .gitmodules. Provides hooks that restore standard git-command ergonomics for embedded children while keeping the child's origin URL out of the public parent repo.",
"type": "module",
"license": "Apache-2.0",
Expand Down Expand Up @@ -87,7 +87,11 @@
"marked-terminal": "^7.3.0"
},
"devDependencies": {
"@cldmv/eslint-plugin-jsonv": "^1.0.3",
"@cldmv/jsonv": "^1.0.2",
"@cldmv/prettier-plugin-jsonv": "^1.0.1",
"@cldmv/vitest-runner": "^1.2.0",
"@eslint/css": "^1.4.0",
"@eslint/js": "^9.18.0",
"@eslint/json": "^2.0.1",
"@eslint/markdown": "^8.0.3",
Expand Down
9 changes: 3 additions & 6 deletions src/api/cli/install-hooks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ export const spec = {
["--yes", "Skip confirmation prompts; assume yes for any modification."],
["--dispatcher-dir <path>", "Override the default dispatcher directory (~/.config/git/hooks)."]
],
examples: [
"$ git-embedded install-hooks",
"$ git-embedded install-hooks --no-symlinks",
"$ git-embedded install-hooks --yes"
]
examples: ["$ git-embedded install-hooks", "$ git-embedded install-hooks --no-symlinks", "$ git-embedded install-hooks --yes"]
};

export async function run(opts = {}) {
Expand Down Expand Up @@ -72,7 +68,8 @@ async function healAndInstall(result, opts) {
{ noSymlinks: opts.noSymlinks }
);
self.report.success(`Healed ${linkResult.created.length} entries`);
if (linkResult.fallbackToCopy.length > 0) self.report.warn(`Filesystem fallback to copy for ${linkResult.fallbackToCopy.length} entries`);
if (linkResult.fallbackToCopy.length > 0)
self.report.warn(`Filesystem fallback to copy for ${linkResult.fallbackToCopy.length} entries`);
} catch (err) {
if (err instanceof CancelledByUser) {
self.report.error(err.message);
Expand Down
3 changes: 2 additions & 1 deletion src/api/cli/restore.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const spec = {
};

const LABEL = {
restored: (r) => `${r.dryRun ? "would restore" : "restored"} ${r.path} from ${r.source} (${r.url})${r.branch ? ` on branch ${r.branch}` : ""}`,
restored: (r) =>
`${r.dryRun ? "would restore" : "restored"} ${r.path} from ${r.source} (${r.url})${r.branch ? ` on branch ${r.branch}` : ""}`,
"already-present": (r) => `${r.path} already present`,
skipped: (r) => `${r.path} skipped`,
unresolved: (r) => `${r.path} unresolved${r.note ? ` β€” ${r.note}` : ""}`,
Expand Down
8 changes: 3 additions & 5 deletions src/api/commander/custom-help.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,12 @@ export function makeCustomHelp(HelpClass, deps) {
const cmdColor = indent === 1 ? chalk.cyan : chalk.blueBright;
output.push(`${pad}${color(cmdColor, term)}`);
if (aliases.length) {
wrapTextWithHangingIndent(aliases.join(", "), indent + 1, "Aliases", (s) => color(chalk.yellow.italic, s)).forEach(
(l) => output.push(l)
wrapTextWithHangingIndent(aliases.join(", "), indent + 1, "Aliases", (s) => color(chalk.yellow.italic, s)).forEach((l) =>
output.push(l)
);
}
if (desc) {
wrapTextWithHangingIndent(desc, indent + 1, "Description", (s) => color(chalk.gray.italic, s)).forEach((l) =>
output.push(l)
);
wrapTextWithHangingIndent(desc, indent + 1, "Description", (s) => color(chalk.gray.italic, s)).forEach((l) => output.push(l));
}
output.push("");

Expand Down
6 changes: 1 addition & 5 deletions src/api/detect/dispatcher.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ export const STANDARD_HOOK_NAMES = [

const DISPATCHER_MARKER = "# git-embedded-compatible dispatcher";

const CHAIN_PATTERNS = [
/exec\s+"?\$repo_hook"?/,
/exec\s+"?\$\{?repo_hook\}?"?/,
/exec\s+"?\$git_dir\/hooks\/\$hook"?/
];
const CHAIN_PATTERNS = [/exec\s+"?\$repo_hook"?/, /exec\s+"?\$\{?repo_hook\}?"?/, /exec\s+"?\$git_dir\/hooks\/\$hook"?/];

function readFull(p) {
try {
Expand Down
Loading
Loading