@indigoai-us/hq-cli 5.345.122 → 5.345.124
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/CHANGELOG.md +25 -0
- package/README.md +15 -0
- package/assets/scaffold/core/scripts/handoff-sync-prefetch.sh +121 -0
- package/assets/scaffold/core/scripts/handoff-sync-publish.sh +124 -0
- package/assets/scaffold/core/scripts/knowledge-pulse-lock.sh +27 -0
- package/assets/scaffold/core/scripts/lib/session-skill-catalog.sh +226 -0
- package/assets/scaffold/core/scripts/skill-installed.sh +43 -0
- package/assets/scaffold/core/scripts/work-mesh-project-registration-offer.sh +77 -0
- package/assets/scaffold/core/scripts/workflow-gate.sh +247 -0
- package/dist/command-catalog.generated.d.ts +332 -0
- package/dist/command-catalog.generated.js +402 -0
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/core-bucket-c-handoff-sync-fast.d.ts +3 -0
- package/dist/commands/core-bucket-c-handoff-sync-fast.js +16 -0
- package/dist/commands/core-bucket-c-handoff-sync-native.d.ts +45 -0
- package/dist/commands/core-bucket-c-handoff-sync-native.js +300 -0
- package/dist/commands/core-bucket-c-small-fast.d.ts +5 -0
- package/dist/commands/core-bucket-c-small-fast.js +19 -0
- package/dist/commands/core-bucket-c-small-native.d.ts +11 -0
- package/dist/commands/core-bucket-c-small-native.js +197 -0
- package/dist/commands/core-bucket-c-work-mesh-offer-native.d.ts +7 -0
- package/dist/commands/core-bucket-c-work-mesh-offer-native.js +147 -0
- package/dist/commands/core-bucket-c-workflow-gate-fast.d.ts +3 -0
- package/dist/commands/core-bucket-c-workflow-gate-fast.js +17 -0
- package/dist/commands/core-bucket-c-workflow-gate-native.d.ts +7 -0
- package/dist/commands/core-bucket-c-workflow-gate-native.js +250 -0
- package/dist/commands/core-derive-trigger-facts-native.js +4 -1
- package/dist/commands/core.js +37 -0
- package/dist/commands/deploy-comments.d.ts +2 -0
- package/dist/commands/deploy-comments.js +2 -2
- package/dist/commands/deploy-list.d.ts +22 -0
- package/dist/commands/deploy-list.js +153 -0
- package/dist/commands/lanes.js +41 -2
- package/dist/commands/mesh.js +457 -16
- package/dist/fast-scaffold.js +74 -0
- package/dist/lib/core-utils/session-company.d.ts +3 -0
- package/dist/lib/core-utils/session-company.js +78 -0
- package/dist/lib/lanes/quota-notice.d.ts +24 -0
- package/dist/lib/lanes/quota-notice.js +61 -0
- package/dist/lib/lanes/quota.d.ts +44 -8
- package/dist/lib/lanes/quota.js +187 -45
- package/dist/lib/lanes/reconcile.js +17 -1
- package/dist/lib/lanes/watcher.js +13 -1
- package/dist/lib/mesh/api.d.ts +2 -0
- package/dist/lib/mesh/api.js +3 -0
- package/dist/lib/mesh/client.d.ts +6 -1
- package/dist/lib/mesh/client.js +18 -5
- package/dist/lib/mesh/team-coordination.d.ts +106 -0
- package/dist/lib/mesh/team-coordination.js +297 -0
- package/dist/lib/work-context/auto-bind.d.ts +75 -0
- package/dist/lib/work-context/auto-bind.js +245 -0
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WMTC-8 gap 7: resolve the project for an unbound (needs_project) session
|
|
3
|
+
* from local evidence only. No network, no subprocess, so a background hook
|
|
4
|
+
* refresh can call it cheaply. Every failure resolves to "no binding".
|
|
5
|
+
*
|
|
6
|
+
* Order:
|
|
7
|
+
* (a) a prior binding for the same repo+branch recorded locally
|
|
8
|
+
* (b) PRDs in the session's company whose repo AND branch match
|
|
9
|
+
* (c) a PRD matching the repo only, when it is the single candidate
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
function readText(file) {
|
|
14
|
+
try {
|
|
15
|
+
return fs.readFileSync(file, "utf8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Find the git checkout for cwd and its current branch without spawning git. */
|
|
22
|
+
export function readGitLocation(cwd) {
|
|
23
|
+
let cur = path.resolve(cwd);
|
|
24
|
+
for (;;) {
|
|
25
|
+
const dotGit = path.join(cur, ".git");
|
|
26
|
+
let st;
|
|
27
|
+
try {
|
|
28
|
+
st = fs.lstatSync(dotGit);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
st = null;
|
|
32
|
+
}
|
|
33
|
+
if (st) {
|
|
34
|
+
let gitDir = dotGit;
|
|
35
|
+
let mainTopLevel;
|
|
36
|
+
if (st.isFile()) {
|
|
37
|
+
const m = /^\s*gitdir:\s*(.+?)\s*$/m.exec(readText(dotGit) ?? "");
|
|
38
|
+
if (!m?.[1])
|
|
39
|
+
return null;
|
|
40
|
+
gitDir = path.resolve(cur, m[1]);
|
|
41
|
+
const common = readText(path.join(gitDir, "commondir"))?.trim();
|
|
42
|
+
if (common)
|
|
43
|
+
mainTopLevel = path.dirname(path.resolve(gitDir, common));
|
|
44
|
+
}
|
|
45
|
+
const head = readText(path.join(gitDir, "HEAD"))?.trim() ?? "";
|
|
46
|
+
const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
|
|
47
|
+
return {
|
|
48
|
+
topLevel: cur,
|
|
49
|
+
...(mainTopLevel && mainTopLevel !== cur ? { mainTopLevel } : {}),
|
|
50
|
+
branch: ref?.[1] ?? null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const parent = path.dirname(cur);
|
|
54
|
+
if (parent === cur)
|
|
55
|
+
return null;
|
|
56
|
+
cur = parent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function normRepoPath(value) {
|
|
60
|
+
return value.trim().replace(/^\.\//, "").replace(/\/+$/, "");
|
|
61
|
+
}
|
|
62
|
+
/** Repo identities for the checkout: HQ-relative paths and absolute paths. */
|
|
63
|
+
export function repoIdentities(loc, hqRoot) {
|
|
64
|
+
const out = new Set();
|
|
65
|
+
for (const top of [loc.topLevel, loc.mainTopLevel]) {
|
|
66
|
+
if (!top)
|
|
67
|
+
continue;
|
|
68
|
+
out.add(normRepoPath(top));
|
|
69
|
+
const rel = path.relative(hqRoot, top);
|
|
70
|
+
if (rel && !rel.startsWith("..") && !path.isAbsolute(rel))
|
|
71
|
+
out.add(normRepoPath(rel));
|
|
72
|
+
}
|
|
73
|
+
return [...out];
|
|
74
|
+
}
|
|
75
|
+
function asRecord(value) {
|
|
76
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
77
|
+
? value
|
|
78
|
+
: null;
|
|
79
|
+
}
|
|
80
|
+
function prdRepoRefs(prd) {
|
|
81
|
+
const meta = asRecord(prd.metadata) ?? {};
|
|
82
|
+
const refs = [];
|
|
83
|
+
const push = (value) => {
|
|
84
|
+
if (typeof value === "string" && value.trim()) {
|
|
85
|
+
refs.push({ path: normRepoPath(value) });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const r = asRecord(value);
|
|
89
|
+
if (r && typeof r.path === "string" && r.path.trim()) {
|
|
90
|
+
refs.push({
|
|
91
|
+
path: normRepoPath(r.path),
|
|
92
|
+
...(typeof r.branch === "string" && r.branch.trim() ? { branch: r.branch.trim() } : {}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
for (const list of [meta.repos, meta.relatedRepos, prd.repos]) {
|
|
97
|
+
if (Array.isArray(list))
|
|
98
|
+
list.forEach(push);
|
|
99
|
+
}
|
|
100
|
+
push(meta.repoPath);
|
|
101
|
+
return refs;
|
|
102
|
+
}
|
|
103
|
+
/** PRDs under companies/<company>/projects/* that name this checkout. */
|
|
104
|
+
export function findRepoPrdCandidates(input) {
|
|
105
|
+
const projectsDir = path.join(input.hqRoot, "companies", input.companySlug, "projects");
|
|
106
|
+
let entries;
|
|
107
|
+
try {
|
|
108
|
+
entries = fs.readdirSync(projectsDir);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const ids = new Set(input.identities);
|
|
114
|
+
const out = [];
|
|
115
|
+
for (const entry of entries.sort()) {
|
|
116
|
+
const prdPath = path.join(projectsDir, entry, "prd.json");
|
|
117
|
+
const raw = readText(prdPath);
|
|
118
|
+
if (!raw)
|
|
119
|
+
continue;
|
|
120
|
+
let prd;
|
|
121
|
+
try {
|
|
122
|
+
prd = asRecord(JSON.parse(raw));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (!prd)
|
|
128
|
+
continue;
|
|
129
|
+
const refs = prdRepoRefs(prd).filter((ref) => ids.has(ref.path));
|
|
130
|
+
if (refs.length === 0)
|
|
131
|
+
continue;
|
|
132
|
+
const prdBranch = typeof prd.branchName === "string" ? prd.branchName.trim() : "";
|
|
133
|
+
const matchedBranch = Boolean(input.branch && (prdBranch === input.branch || refs.some((ref) => ref.branch === input.branch)));
|
|
134
|
+
const meta = asRecord(prd.metadata);
|
|
135
|
+
const projectId = (typeof prd.projectId === "string" && prd.projectId.trim())
|
|
136
|
+
|| (typeof meta?.projectId === "string" && meta.projectId.trim())
|
|
137
|
+
|| entry;
|
|
138
|
+
out.push({ projectId, prdPath, matchedBranch });
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
export function autoBindHistoryPath(workContextRoot) {
|
|
143
|
+
return path.join(workContextRoot, "auto-bind-history.json");
|
|
144
|
+
}
|
|
145
|
+
function historyKey(companySlug, repo, branch) {
|
|
146
|
+
return `${companySlug}|${repo}@${branch}`;
|
|
147
|
+
}
|
|
148
|
+
function readHistory(workContextRoot) {
|
|
149
|
+
const raw = readText(autoBindHistoryPath(workContextRoot));
|
|
150
|
+
if (!raw)
|
|
151
|
+
return {};
|
|
152
|
+
try {
|
|
153
|
+
return (asRecord(JSON.parse(raw)) ?? {});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export function lookupPriorBinding(input) {
|
|
160
|
+
if (!input.branch)
|
|
161
|
+
return null;
|
|
162
|
+
const history = readHistory(input.workContextRoot);
|
|
163
|
+
for (const repo of input.identities) {
|
|
164
|
+
const hit = history[historyKey(input.companySlug, repo, input.branch)];
|
|
165
|
+
if (hit?.projectId)
|
|
166
|
+
return hit.projectId;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
/** Remember a binding for this repo+branch. Best-effort; never throws. */
|
|
171
|
+
export function recordPriorBinding(input) {
|
|
172
|
+
if (!input.branch || input.identities.length === 0)
|
|
173
|
+
return;
|
|
174
|
+
try {
|
|
175
|
+
const history = readHistory(input.workContextRoot);
|
|
176
|
+
const at = (input.now ?? new Date()).toISOString();
|
|
177
|
+
for (const repo of input.identities) {
|
|
178
|
+
history[historyKey(input.companySlug, repo, input.branch)] = {
|
|
179
|
+
projectId: input.projectId,
|
|
180
|
+
companySlug: input.companySlug,
|
|
181
|
+
at,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const file = autoBindHistoryPath(input.workContextRoot);
|
|
185
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
186
|
+
const tmp = `${file}.tmp.${process.pid}`;
|
|
187
|
+
fs.writeFileSync(tmp, `${JSON.stringify(history, null, 2)}\n`, { mode: 0o600 });
|
|
188
|
+
fs.renameSync(tmp, file);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* history is a hint only */
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Decision
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
export function decideAutoBind(input) {
|
|
198
|
+
const { state } = input;
|
|
199
|
+
if (!state)
|
|
200
|
+
return { action: "noop", reason: "no_session" };
|
|
201
|
+
if (state.contextStatus !== "needs_project") {
|
|
202
|
+
if (state.projectId)
|
|
203
|
+
return { action: "noop", reason: "already_bound", projectId: state.projectId };
|
|
204
|
+
return { action: "noop", reason: "not_needs_project" };
|
|
205
|
+
}
|
|
206
|
+
const companySlug = input.companySlug?.trim() || state.companySlug?.trim();
|
|
207
|
+
if (!companySlug)
|
|
208
|
+
return { action: "skip", reason: "no_company", candidates: [] };
|
|
209
|
+
const git = readGitLocation(input.cwd);
|
|
210
|
+
if (!git)
|
|
211
|
+
return { action: "skip", reason: "no_git", candidates: [] };
|
|
212
|
+
const identities = repoIdentities(git, input.hqRoot);
|
|
213
|
+
const extra = { git, identities };
|
|
214
|
+
const prior = lookupPriorBinding({
|
|
215
|
+
workContextRoot: input.workContextRoot,
|
|
216
|
+
companySlug,
|
|
217
|
+
identities,
|
|
218
|
+
branch: git.branch,
|
|
219
|
+
});
|
|
220
|
+
const candidates = findRepoPrdCandidates({
|
|
221
|
+
hqRoot: input.hqRoot,
|
|
222
|
+
companySlug,
|
|
223
|
+
identities,
|
|
224
|
+
branch: git.branch,
|
|
225
|
+
});
|
|
226
|
+
if (prior)
|
|
227
|
+
return { action: "bind", projectId: prior, source: "history", candidates, ...extra };
|
|
228
|
+
const branchMatches = candidates.filter((c) => c.matchedBranch);
|
|
229
|
+
if (branchMatches.length === 1) {
|
|
230
|
+
return { action: "bind", projectId: branchMatches[0].projectId, source: "repo_branch", candidates, ...extra };
|
|
231
|
+
}
|
|
232
|
+
if (branchMatches.length > 1) {
|
|
233
|
+
return { action: "skip", reason: "ambiguous", candidates: branchMatches, ...extra };
|
|
234
|
+
}
|
|
235
|
+
if (candidates.length === 1) {
|
|
236
|
+
return { action: "bind", projectId: candidates[0].projectId, source: "repo_only", candidates, ...extra };
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
action: "skip",
|
|
240
|
+
reason: candidates.length ? "ambiguous" : "no_match",
|
|
241
|
+
candidates,
|
|
242
|
+
...extra,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=auto-bind.js.map
|