@indigoai-us/hq-cloud 5.31.0 → 5.32.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.
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +69 -2
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +292 -0
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/share.d.ts +26 -0
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +105 -8
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +210 -0
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +28 -2
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +25 -4
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +82 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/describe-error.d.ts +21 -0
- package/dist/lib/describe-error.d.ts.map +1 -0
- package/dist/lib/describe-error.js +53 -0
- package/dist/lib/describe-error.js.map +1 -0
- package/dist/lib/describe-error.test.d.ts +2 -0
- package/dist/lib/describe-error.test.d.ts.map +1 -0
- package/dist/lib/describe-error.test.js +89 -0
- package/dist/lib/describe-error.test.js.map +1 -0
- package/dist/personal-vault.d.ts +63 -7
- package/dist/personal-vault.d.ts.map +1 -1
- package/dist/personal-vault.js +112 -8
- package/dist/personal-vault.js.map +1 -1
- package/dist/personal-vault.test.d.ts +14 -0
- package/dist/personal-vault.test.d.ts.map +1 -0
- package/dist/personal-vault.test.js +191 -0
- package/dist/personal-vault.test.js.map +1 -0
- package/package.json +1 -1
- package/src/bin/sync-runner.test.ts +364 -0
- package/src/bin/sync-runner.ts +73 -2
- package/src/cli/share.test.ts +243 -0
- package/src/cli/share.ts +142 -8
- package/src/cli/sync.test.ts +91 -0
- package/src/cli/sync.ts +56 -4
- package/src/index.ts +3 -0
- package/src/lib/describe-error.test.ts +100 -0
- package/src/lib/describe-error.ts +58 -0
- package/src/personal-vault.test.ts +231 -0
- package/src/personal-vault.ts +134 -8
package/dist/personal-vault.js
CHANGED
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
* - `.git`: a git repo's own metadata is hostile to multi-machine
|
|
15
15
|
* sync; .gitignore alone doesn't cover `.git/` because it's the repo
|
|
16
16
|
* itself, not a tracked path.
|
|
17
|
-
* - `companies/`:
|
|
18
|
-
*
|
|
17
|
+
* - `companies/`: the top-level `companies/` directory is never enumerated
|
|
18
|
+
* wholesale. Team-backed companies are synced by the runner's
|
|
19
|
+
* per-membership fanout (one bucket per company). Individual
|
|
20
|
+
* `companies/{slug}/` subdirs MAY be added back via
|
|
21
|
+
* `computePersonalCompanySubdirs()` for companies that explicitly
|
|
22
|
+
* declare `cloud: false` in `company.yaml` and are not in the operator's
|
|
23
|
+
* team-synced membership set — those land under the personal bucket as
|
|
24
|
+
* `companies/{slug}/...` keys.
|
|
19
25
|
* - `repos/`, `workspace/`: per user directive — heavy local-only
|
|
20
26
|
* content (cloned remotes, session threads) that has no business in
|
|
21
27
|
* the personal vault.
|
|
@@ -38,16 +44,31 @@ export const PERSONAL_VAULT_EXCLUDED_TOP_LEVEL = [
|
|
|
38
44
|
"workspace",
|
|
39
45
|
];
|
|
40
46
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
47
|
+
* Company slugs that are never eligible for the personal-bucket fallback,
|
|
48
|
+
* regardless of their `cloud:` marker. `_template` is the scaffolding
|
|
49
|
+
* source for `/newcompany` — copying it into the personal vault would
|
|
50
|
+
* pollute every machine's vault with the template tree.
|
|
51
|
+
*/
|
|
52
|
+
export const PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS = [
|
|
53
|
+
"_template",
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* Compute absolute paths to share for the personal vault.
|
|
57
|
+
*
|
|
58
|
+
* Two sources are concatenated:
|
|
59
|
+
* 1. Every top-level entry under `hqRoot` whose basename is NOT in
|
|
60
|
+
* `PERSONAL_VAULT_EXCLUDED_TOP_LEVEL`.
|
|
61
|
+
* 2. Every `companies/{slug}/` subdir that opts into the personal
|
|
62
|
+
* bucket via `company.yaml: cloud: false`, after excluding (a)
|
|
63
|
+
* `_template`, (b) slugs already in `opts.teamSyncedSlugs`, and
|
|
64
|
+
* (c) any subdir without an explicit `cloud: false` marker.
|
|
65
|
+
*
|
|
45
66
|
* Order is whatever `fs.readdirSync` returns — share() doesn't care, and
|
|
46
67
|
* the per-file walk inside share() handles recursion uniformly. Missing
|
|
47
68
|
* hqRoot returns []; callers treat that as "no personal content to push"
|
|
48
69
|
* rather than a hard error.
|
|
49
70
|
*/
|
|
50
|
-
export function computePersonalVaultPaths(hqRoot) {
|
|
71
|
+
export function computePersonalVaultPaths(hqRoot, opts = {}) {
|
|
51
72
|
let entries;
|
|
52
73
|
try {
|
|
53
74
|
entries = fs.readdirSync(hqRoot);
|
|
@@ -55,8 +76,91 @@ export function computePersonalVaultPaths(hqRoot) {
|
|
|
55
76
|
catch {
|
|
56
77
|
return [];
|
|
57
78
|
}
|
|
58
|
-
|
|
79
|
+
const topLevel = entries
|
|
59
80
|
.filter((name) => !PERSONAL_VAULT_EXCLUDED_TOP_LEVEL.includes(name))
|
|
60
81
|
.map((name) => path.join(hqRoot, name));
|
|
82
|
+
const companySubdirs = opts.includeLocalCompanies === true
|
|
83
|
+
? computePersonalCompanySubdirs(hqRoot, opts.teamSyncedSlugs)
|
|
84
|
+
: [];
|
|
85
|
+
return [...topLevel, ...companySubdirs];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Discover `companies/{slug}/` subdirs that should sync to the personal
|
|
89
|
+
* bucket as a fallback for companies the operator has not designated as
|
|
90
|
+
* team-backed. Filter rules (all must hold):
|
|
91
|
+
*
|
|
92
|
+
* 1. The subdir is a directory (skip stray files).
|
|
93
|
+
* 2. The slug is NOT in `PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS`
|
|
94
|
+
* (currently just `_template`).
|
|
95
|
+
* 3. The slug is NOT in `teamSyncedSlugs` (membership-backed slugs sync
|
|
96
|
+
* to their own bucket and must not be double-written).
|
|
97
|
+
* 4. `companies/{slug}/company.yaml` exists and contains a
|
|
98
|
+
* `cloud: false` line. A missing file or `cloud: true` opts the
|
|
99
|
+
* directory OUT of the personal vault — silent inclusion would
|
|
100
|
+
* capture scratch/dead dirs the user never intended to ship.
|
|
101
|
+
*
|
|
102
|
+
* Returns absolute paths.
|
|
103
|
+
*/
|
|
104
|
+
export function computePersonalCompanySubdirs(hqRoot, teamSyncedSlugs = new Set()) {
|
|
105
|
+
const companiesRoot = path.join(hqRoot, "companies");
|
|
106
|
+
let slugs;
|
|
107
|
+
try {
|
|
108
|
+
slugs = fs.readdirSync(companiesRoot);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const eligible = [];
|
|
114
|
+
for (const slug of slugs) {
|
|
115
|
+
if (PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS.includes(slug))
|
|
116
|
+
continue;
|
|
117
|
+
if (teamSyncedSlugs.has(slug))
|
|
118
|
+
continue;
|
|
119
|
+
const subdir = path.join(companiesRoot, slug);
|
|
120
|
+
let isDir = false;
|
|
121
|
+
try {
|
|
122
|
+
isDir = fs.statSync(subdir).isDirectory();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (!isDir)
|
|
128
|
+
continue;
|
|
129
|
+
if (!companyHasCloudFalseMarker(subdir))
|
|
130
|
+
continue;
|
|
131
|
+
eligible.push(subdir);
|
|
132
|
+
}
|
|
133
|
+
return eligible;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* True iff `{subdir}/company.yaml` exists and contains an explicit
|
|
137
|
+
* top-level `cloud: false` line. Matches the canonical shape
|
|
138
|
+
* `/designate-team` writes — that command's awk is the only producer of
|
|
139
|
+
* this key, and it always emits `cloud: <bool>` at column zero with no
|
|
140
|
+
* indentation, no quoting, lowercase boolean, optional trailing comment.
|
|
141
|
+
*
|
|
142
|
+
* Anchored at column 0 deliberately:
|
|
143
|
+
* - rejects `meta:\n cloud: false` (nested key, would be `meta.cloud`
|
|
144
|
+
* in a real YAML parser — false positive for us if we allowed
|
|
145
|
+
* arbitrary leading whitespace)
|
|
146
|
+
* - rejects `- cloud: false` (list item, also a nested key)
|
|
147
|
+
* - rejects flow mappings like `{ cloud: false }`
|
|
148
|
+
*
|
|
149
|
+
* Other intentional rejections (lowercase `false` only, no YAML-alt
|
|
150
|
+
* boolean spellings): `cloud: False`, `cloud: FALSE`, `cloud: no`,
|
|
151
|
+
* `cloud: off`, `cloud: "false"`, `cloud: 'false'`. None of these are
|
|
152
|
+
* produced by `/designate-team`; matching them risks confusing
|
|
153
|
+
* round-trips with hand-edited YAML.
|
|
154
|
+
*/
|
|
155
|
+
function companyHasCloudFalseMarker(subdir) {
|
|
156
|
+
const yamlPath = path.join(subdir, "company.yaml");
|
|
157
|
+
let text;
|
|
158
|
+
try {
|
|
159
|
+
text = fs.readFileSync(yamlPath, "utf8");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return /^cloud:\s*false\b/m.test(text);
|
|
61
165
|
}
|
|
62
166
|
//# sourceMappingURL=personal-vault.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"personal-vault.js","sourceRoot":"","sources":["../src/personal-vault.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"personal-vault.js","sourceRoot":"","sources":["../src/personal-vault.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the personal-vault path-discovery helpers.
|
|
3
|
+
*
|
|
4
|
+
* These cover the column-anchored `cloud: false` marker regex and the
|
|
5
|
+
* `companies/{slug}/` enumeration filter — both are pure functions over
|
|
6
|
+
* disk + caller-supplied option sets, so we exercise them against tmp
|
|
7
|
+
* directories rather than mocking fs.
|
|
8
|
+
*
|
|
9
|
+
* The sync-runner-level integration (env-var gate, team-synced slug
|
|
10
|
+
* derivation, end-to-end share() invocation) lives in
|
|
11
|
+
* `bin/sync-runner.test.ts` — tests G, H, I.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=personal-vault.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personal-vault.test.d.ts","sourceRoot":"","sources":["../src/personal-vault.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the personal-vault path-discovery helpers.
|
|
3
|
+
*
|
|
4
|
+
* These cover the column-anchored `cloud: false` marker regex and the
|
|
5
|
+
* `companies/{slug}/` enumeration filter — both are pure functions over
|
|
6
|
+
* disk + caller-supplied option sets, so we exercise them against tmp
|
|
7
|
+
* directories rather than mocking fs.
|
|
8
|
+
*
|
|
9
|
+
* The sync-runner-level integration (env-var gate, team-synced slug
|
|
10
|
+
* derivation, end-to-end share() invocation) lives in
|
|
11
|
+
* `bin/sync-runner.test.ts` — tests G, H, I.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as os from "os";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import { computePersonalCompanySubdirs, computePersonalVaultPaths, PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS, PERSONAL_VAULT_EXCLUDED_TOP_LEVEL, } from "./personal-vault.js";
|
|
18
|
+
describe("personal-vault helpers", () => {
|
|
19
|
+
let hqRoot;
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-pv-test-"));
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
25
|
+
});
|
|
26
|
+
/** Helper: write `companies/{slug}/company.yaml` with arbitrary content. */
|
|
27
|
+
function writeCompany(slug, yaml) {
|
|
28
|
+
const dir = path.join(hqRoot, "companies", slug);
|
|
29
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
30
|
+
if (yaml !== null)
|
|
31
|
+
fs.writeFileSync(path.join(dir, "company.yaml"), yaml);
|
|
32
|
+
}
|
|
33
|
+
/** Helper: relative-paths-sorted projection of an absolute-path list. */
|
|
34
|
+
function rel(abs) {
|
|
35
|
+
return abs.map((p) => path.relative(hqRoot, p)).sort();
|
|
36
|
+
}
|
|
37
|
+
// ── Exported constants ─────────────────────────────────────────────────
|
|
38
|
+
it("constants: PERSONAL_VAULT_EXCLUDED_TOP_LEVEL contains the canonical four names", () => {
|
|
39
|
+
expect([...PERSONAL_VAULT_EXCLUDED_TOP_LEVEL].sort()).toEqual([
|
|
40
|
+
".git",
|
|
41
|
+
"companies",
|
|
42
|
+
"repos",
|
|
43
|
+
"workspace",
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
it("constants: PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS hard-lists `_template`", () => {
|
|
47
|
+
expect(PERSONAL_VAULT_COMPANY_EXCLUDED_SLUGS).toContain("_template");
|
|
48
|
+
});
|
|
49
|
+
// ── computePersonalCompanySubdirs: marker-regex acceptance ─────────────
|
|
50
|
+
it("marker: accepts canonical `cloud: false` at column 0", () => {
|
|
51
|
+
writeCompany("foo", "cloud: false\nname: Foo\n");
|
|
52
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
53
|
+
path.join("companies", "foo"),
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
it("marker: accepts `cloud:false` (no space after colon)", () => {
|
|
57
|
+
writeCompany("foo", "cloud:false\n");
|
|
58
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
59
|
+
path.join("companies", "foo"),
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
it("marker: accepts trailing comment after `cloud: false`", () => {
|
|
63
|
+
writeCompany("foo", "cloud: false # local-only, never upload to team\n");
|
|
64
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
65
|
+
path.join("companies", "foo"),
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
it("marker: accepts CRLF line endings", () => {
|
|
69
|
+
writeCompany("foo", "cloud: false\r\nname: Foo\r\n");
|
|
70
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
71
|
+
path.join("companies", "foo"),
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
it("marker: accepts `cloud: false` even when it is NOT the first line", () => {
|
|
75
|
+
writeCompany("foo", "name: Foo\nslug: foo\ncloud: false\n");
|
|
76
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
77
|
+
path.join("companies", "foo"),
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
// ── computePersonalCompanySubdirs: marker-regex rejection ──────────────
|
|
81
|
+
it("marker: rejects `cloud: true`", () => {
|
|
82
|
+
writeCompany("foo", "cloud: true\n");
|
|
83
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
it("marker: rejects missing company.yaml entirely", () => {
|
|
86
|
+
writeCompany("foo", null);
|
|
87
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
it("marker: rejects empty company.yaml", () => {
|
|
90
|
+
writeCompany("foo", "");
|
|
91
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
it("marker: rejects nested `cloud: false` (column-0 anchor — false-positive guard)", () => {
|
|
94
|
+
// Regression for an audit finding: an earlier `^[ \t]*cloud:` regex
|
|
95
|
+
// allowed arbitrary leading whitespace, which silently matched
|
|
96
|
+
// nested keys like `meta.cloud = false`. The tightened `^cloud:` regex
|
|
97
|
+
// rejects this, matching the canonical column-0 shape that
|
|
98
|
+
// `/designate-team`'s awk emits.
|
|
99
|
+
writeCompany("foo", "meta:\n cloud: false\n");
|
|
100
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
it("marker: rejects list-item style `- cloud: false`", () => {
|
|
103
|
+
writeCompany("foo", "tags:\n- cloud: false\n");
|
|
104
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
it("marker: rejects flow-mapping `{ cloud: false }`", () => {
|
|
107
|
+
writeCompany("foo", "{ cloud: false }\n");
|
|
108
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
it("marker: rejects YAML-alt boolean spellings (False, FALSE, no, off)", () => {
|
|
111
|
+
for (const variant of ["cloud: False\n", "cloud: FALSE\n", "cloud: no\n", "cloud: off\n"]) {
|
|
112
|
+
writeCompany("foo", variant);
|
|
113
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
114
|
+
fs.rmSync(path.join(hqRoot, "companies", "foo"), { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
it("marker: rejects quoted `cloud: \"false\"` (the quote breaks the literal-false match)", () => {
|
|
118
|
+
writeCompany("foo", 'cloud: "false"\n');
|
|
119
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
it("marker: rejects `cloud: falsehood` (word-boundary guard)", () => {
|
|
122
|
+
writeCompany("foo", "cloud: falsehood\n");
|
|
123
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
124
|
+
});
|
|
125
|
+
// ── Subdir-level filters ───────────────────────────────────────────────
|
|
126
|
+
it("filter: skips _template even with cloud:false marker", () => {
|
|
127
|
+
writeCompany("_template", "cloud: false\n");
|
|
128
|
+
writeCompany("real", "cloud: false\n");
|
|
129
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
130
|
+
path.join("companies", "real"),
|
|
131
|
+
]);
|
|
132
|
+
});
|
|
133
|
+
it("filter: skips slugs in the teamSyncedSlugs set", () => {
|
|
134
|
+
writeCompany("free", "cloud: false\n");
|
|
135
|
+
writeCompany("team-acme", "cloud: false\n");
|
|
136
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot, new Set(["team-acme"])))).toEqual([path.join("companies", "free")]);
|
|
137
|
+
});
|
|
138
|
+
it("filter: skips stray files under companies/ (only directories considered)", () => {
|
|
139
|
+
// Hypothetical: someone drops a README.md at companies/. Should not
|
|
140
|
+
// crash, should not be enumerated as a company subdir.
|
|
141
|
+
fs.mkdirSync(path.join(hqRoot, "companies"), { recursive: true });
|
|
142
|
+
fs.writeFileSync(path.join(hqRoot, "companies", "README.md"), "# README");
|
|
143
|
+
writeCompany("real", "cloud: false\n");
|
|
144
|
+
expect(rel(computePersonalCompanySubdirs(hqRoot))).toEqual([
|
|
145
|
+
path.join("companies", "real"),
|
|
146
|
+
]);
|
|
147
|
+
});
|
|
148
|
+
it("filter: missing hqRoot/companies/ returns []", () => {
|
|
149
|
+
// No `companies/` dir at all (fresh install or atypical layout).
|
|
150
|
+
expect(computePersonalCompanySubdirs(hqRoot)).toEqual([]);
|
|
151
|
+
});
|
|
152
|
+
// ── computePersonalVaultPaths: top-level + company-subdir composition ──
|
|
153
|
+
it("composition: includeLocalCompanies=false skips companies/ entirely", () => {
|
|
154
|
+
fs.mkdirSync(path.join(hqRoot, ".claude"));
|
|
155
|
+
fs.mkdirSync(path.join(hqRoot, "knowledge"));
|
|
156
|
+
writeCompany("foo", "cloud: false\n");
|
|
157
|
+
const out = rel(computePersonalVaultPaths(hqRoot));
|
|
158
|
+
// Top-level entries present, no companies/* subdir.
|
|
159
|
+
expect(out).toContain(".claude");
|
|
160
|
+
expect(out).toContain("knowledge");
|
|
161
|
+
expect(out.some((p) => p.startsWith("companies"))).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
it("composition: includeLocalCompanies=true adds opt-in company subdirs", () => {
|
|
164
|
+
fs.mkdirSync(path.join(hqRoot, "knowledge"));
|
|
165
|
+
writeCompany("foo", "cloud: false\n");
|
|
166
|
+
writeCompany("bar", "cloud: true\n");
|
|
167
|
+
const out = rel(computePersonalVaultPaths(hqRoot, { includeLocalCompanies: true }));
|
|
168
|
+
expect(out).toContain("knowledge");
|
|
169
|
+
expect(out).toContain(path.join("companies", "foo"));
|
|
170
|
+
expect(out).not.toContain(path.join("companies", "bar"));
|
|
171
|
+
// The companies/ top-level entry itself is never present — only specific
|
|
172
|
+
// opted-in subdirs. Preserves the invariant that share() never walks
|
|
173
|
+
// the whole companies/ tree under personalMode.
|
|
174
|
+
expect(out).not.toContain("companies");
|
|
175
|
+
});
|
|
176
|
+
it("composition: includeLocalCompanies=true + teamSyncedSlugs filter combine correctly", () => {
|
|
177
|
+
writeCompany("foo", "cloud: false\n");
|
|
178
|
+
writeCompany("synced", "cloud: false\n");
|
|
179
|
+
const out = rel(computePersonalVaultPaths(hqRoot, {
|
|
180
|
+
includeLocalCompanies: true,
|
|
181
|
+
teamSyncedSlugs: new Set(["synced"]),
|
|
182
|
+
}));
|
|
183
|
+
expect(out).toContain(path.join("companies", "foo"));
|
|
184
|
+
expect(out).not.toContain(path.join("companies", "synced"));
|
|
185
|
+
});
|
|
186
|
+
it("composition: missing hqRoot returns []", () => {
|
|
187
|
+
fs.rmSync(hqRoot, { recursive: true });
|
|
188
|
+
expect(computePersonalVaultPaths(hqRoot, { includeLocalCompanies: true })).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=personal-vault.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personal-vault.test.js","sourceRoot":"","sources":["../src/personal-vault.test.ts"],"names":[],"mappings":"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"}
|