@smartmemory/compose 0.1.38-beta → 0.1.40-beta
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/bin/compose.js +3 -1
- package/bin/git-hooks/pre-push.template +8 -0
- package/contracts/feature-json.schema.json +54 -4
- package/contracts/judge-result.json +27 -4
- package/dist/assets/{App-BOmZUQK9.js → App-DAlZEv-O.js} +163 -163
- package/dist/assets/{arc-BFMYxU8d.js → arc-CJH3_faQ.js} +1 -1
- package/dist/assets/{architectureDiagram-3BPJPVTR-Dr1TLm4n.js → architectureDiagram-3BPJPVTR-CcbjDL0X.js} +1 -1
- package/dist/assets/{blockDiagram-GPEHLZMM-DfiOIWI5.js → blockDiagram-GPEHLZMM-CUfnQl0d.js} +1 -1
- package/dist/assets/{c4Diagram-AAUBKEIU-CWZPV6Gi.js → c4Diagram-AAUBKEIU-BmyI1ykb.js} +1 -1
- package/dist/assets/channel-ZbvrEgVw.js +1 -0
- package/dist/assets/{chunk-2J33WTMH-DsfEX96t.js → chunk-2J33WTMH-BBqApkEh.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-BYmdEdqH.js → chunk-4BX2VUAB-CH3WPC9v.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-C074eKNZ.js → chunk-55IACEB6-DSF1sU8o.js} +1 -1
- package/dist/assets/{chunk-727SXJPM-rxaajQx0.js → chunk-727SXJPM-HjTVn9xp.js} +1 -1
- package/dist/assets/{chunk-AQP2D5EJ-Cg6-5M28.js → chunk-AQP2D5EJ-C2ZGpxjS.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-FSbyjfSh.js → chunk-FMBD7UC4-CCfvaMbx.js} +1 -1
- package/dist/assets/{chunk-ND2GUHAM-DhD_mt3D.js → chunk-ND2GUHAM-Ke_5zZ4E.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-LMU-D6pF.js → chunk-QZHKN3VN-D_3X_vH5.js} +1 -1
- package/dist/assets/classDiagram-4FO5ZUOK-CIpDpiGU.js +1 -0
- package/dist/assets/classDiagram-v2-Q7XG4LA2-CIpDpiGU.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CknvjYWn.js → cose-bilkent-S5V4N54A-BavWz_Ts.js} +1 -1
- package/dist/assets/{dagre-BM42HDAG-BWNVctIO.js → dagre-BM42HDAG-CJN2rIrd.js} +1 -1
- package/dist/assets/{diagram-2AECGRRQ-DhPeu8mD.js → diagram-2AECGRRQ-DFSD9lyx.js} +1 -1
- package/dist/assets/{diagram-5GNKFQAL-B8e2ppCQ.js → diagram-5GNKFQAL-B2Iz6xSI.js} +1 -1
- package/dist/assets/{diagram-KO2AKTUF-CJ2bkC5P.js → diagram-KO2AKTUF-BF77NHeO.js} +1 -1
- package/dist/assets/{diagram-LMA3HP47-CCyEuSkM.js → diagram-LMA3HP47-B9rERFCD.js} +1 -1
- package/dist/assets/{diagram-OG6HWLK6-CDe7c8Ym.js → diagram-OG6HWLK6-CIS_DZW7.js} +1 -1
- package/dist/assets/{erDiagram-TEJ5UH35-BDTpNyUZ.js → erDiagram-TEJ5UH35-BubvCPWm.js} +1 -1
- package/dist/assets/{flowDiagram-I6XJVG4X-CSEQ9sFL.js → flowDiagram-I6XJVG4X-CVjDg-md.js} +1 -1
- package/dist/assets/{ganttDiagram-6RSMTGT7-CJI3ZQCl.js → ganttDiagram-6RSMTGT7-rdIEkSjo.js} +1 -1
- package/dist/assets/{gitGraphDiagram-PVQCEYII-BmIOl3oN.js → gitGraphDiagram-PVQCEYII-Bv7mceGn.js} +1 -1
- package/dist/assets/{index-DHyg44Px.js → index-BPSrV_ie.js} +2 -2
- package/dist/assets/{infoDiagram-5YYISTIA-BiJXa7xo.js → infoDiagram-5YYISTIA-BhwEnN8y.js} +1 -1
- package/dist/assets/{ishikawaDiagram-YF4QCWOH-D2S-R9bH.js → ishikawaDiagram-YF4QCWOH-Bc3cYCKN.js} +1 -1
- package/dist/assets/{journeyDiagram-JHISSGLW-Dk6NJYdI.js → journeyDiagram-JHISSGLW-DA82WE4d.js} +1 -1
- package/dist/assets/{kanban-definition-UN3LZRKU-DrTme_f5.js → kanban-definition-UN3LZRKU-CS3ptTQL.js} +1 -1
- package/dist/assets/{linear-DAL46yON.js → linear-VVoHRBmK.js} +1 -1
- package/dist/assets/{mindmap-definition-RKZ34NQL-lzCtbkrz.js → mindmap-definition-RKZ34NQL-Dx93Nhkp.js} +1 -1
- package/dist/assets/{pieDiagram-4H26LBE5-BGGc2f7U.js → pieDiagram-4H26LBE5-CO107nas.js} +1 -1
- package/dist/assets/{quadrantDiagram-W4KKPZXB-DN8kBgF9.js → quadrantDiagram-W4KKPZXB-CrMXIHGP.js} +1 -1
- package/dist/assets/{requirementDiagram-4Y6WPE33-CqTXzyF-.js → requirementDiagram-4Y6WPE33-Jhy0JAv1.js} +1 -1
- package/dist/assets/{sankeyDiagram-5OEKKPKP-l5oIrkxp.js → sankeyDiagram-5OEKKPKP-Byqar1oo.js} +1 -1
- package/dist/assets/{sequenceDiagram-3UESZ5HK-B5mMGwpr.js → sequenceDiagram-3UESZ5HK-BXn-Yp_S.js} +1 -1
- package/dist/assets/{stateDiagram-AJRCARHV-B8yZRkdR.js → stateDiagram-AJRCARHV-BsH4tWQo.js} +1 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-CW-BZJp6.js +1 -0
- package/dist/assets/{timeline-definition-PNZ67QCA-uc4a9RNG.js → timeline-definition-PNZ67QCA-RaJbKHNh.js} +1 -1
- package/dist/assets/{vennDiagram-CIIHVFJN-Dxp9IL4U.js → vennDiagram-CIIHVFJN-Cq50SepU.js} +1 -1
- package/dist/assets/{wardley-L42UT6IY-1u4vjxUt.js → wardley-L42UT6IY-CfLD0dlo.js} +1 -1
- package/dist/assets/{wardleyDiagram-YWT4CUSO-DWpWWd1b.js → wardleyDiagram-YWT4CUSO-O_yLrImv.js} +1 -1
- package/dist/assets/{xychartDiagram-2RQKCTM6-Cpcj31O3.js → xychartDiagram-2RQKCTM6-DnL9O2jh.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/feature-validator.js +373 -3
- package/lib/feature-writer.js +158 -1
- package/lib/tracker/github-api.js +8 -1
- package/lib/xref-citation.js +235 -0
- package/package.json +1 -1
- package/server/compose-mcp-tools.js +2 -1
- package/server/compose-mcp.js +11 -5
- package/dist/assets/channel-GvJclmQg.js +0 -1
- package/dist/assets/classDiagram-4FO5ZUOK-DV6SPGMl.js +0 -1
- package/dist/assets/classDiagram-v2-Q7XG4LA2-DV6SPGMl.js +0 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-BK-1uf4O.js +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xref-citation.js — pure parser for inline cross-project external-reference
|
|
3
|
+
* citations embedded in a ROADMAP/description cell (COMP-MCP-XREF-SCHEMA, #15).
|
|
4
|
+
*
|
|
5
|
+
* A citation is an HTML comment so it renders invisibly in markdown:
|
|
6
|
+
*
|
|
7
|
+
* <!-- xref: github owner/repo#123 expect=open -->
|
|
8
|
+
* <!-- xref: github smartmemory/compose#7 expect=closed note="shipped X" -->
|
|
9
|
+
* <!-- xref: local compose COMP-MCP-VALIDATE expect=COMPLETE -->
|
|
10
|
+
* <!-- xref: url https://example.com/spec note="design ref" -->
|
|
11
|
+
*
|
|
12
|
+
* Grammar (spec §3.1 EBNF):
|
|
13
|
+
* citation = "<!--" ws "xref:" ws provider ws target
|
|
14
|
+
* [ ws "expect=" expect ] [ ws "note=" qstring ] ws "-->"
|
|
15
|
+
* provider = "github" | "local" | "url" ; resolvable
|
|
16
|
+
* | "jira" | "linear" | "notion" | "obsidian" ; reserved url-class
|
|
17
|
+
* gh_target = repo "#" issue ; repo = owner "/" name
|
|
18
|
+
* local_target = repo_token ws feature_code
|
|
19
|
+
* url_target = URL ; url + every reserved provider
|
|
20
|
+
*
|
|
21
|
+
* This module performs ZERO I/O and ZERO network. It is a pure
|
|
22
|
+
* string → object function. Consumed by #16 (`runExternalRefChecks`); #15
|
|
23
|
+
* ships it standalone with no caller in the validator path.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// url-class = `url` + every reserved provider (carry a url_target, never
|
|
27
|
+
// resolved in v1). ALL_PROVIDERS is the full accepted set.
|
|
28
|
+
const RESOLVABLE_PROVIDERS = ['github', 'local', 'url'];
|
|
29
|
+
const RESERVED_PROVIDERS = ['jira', 'linear', 'notion', 'obsidian'];
|
|
30
|
+
const ALL_PROVIDERS = new Set([...RESOLVABLE_PROVIDERS, ...RESERVED_PROVIDERS]);
|
|
31
|
+
|
|
32
|
+
const GITHUB_EXPECT = new Set(['open', 'closed']);
|
|
33
|
+
const LOCAL_EXPECT = new Set([
|
|
34
|
+
'PLANNED', 'IN_PROGRESS', 'PARTIAL', 'COMPLETE',
|
|
35
|
+
'SUPERSEDED', 'PARKED', 'BLOCKED', 'KILLED',
|
|
36
|
+
]);
|
|
37
|
+
const FEATURE_CODE_RE = /^[A-Z][A-Z0-9-]*[A-Z0-9]$/;
|
|
38
|
+
const URI_SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+\-.]*:\/\//;
|
|
39
|
+
|
|
40
|
+
// Anchored scan: only HTML comments whose body begins with `xref:` are
|
|
41
|
+
// considered. Any other `<!-- ... -->` is ignored entirely.
|
|
42
|
+
const CITATION_RE = /<!--\s*xref:\s*([\s\S]*?)\s*-->/g;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Structured parse error for a comment that matched `<!--\s*xref:` but
|
|
46
|
+
* failed the grammar. Consumed by #16 as the `XREF_MALFORMED` finding;
|
|
47
|
+
* #15 only surfaces it via the return value (never throws, never logs).
|
|
48
|
+
*/
|
|
49
|
+
export class ParseError {
|
|
50
|
+
constructor(raw, reason) {
|
|
51
|
+
this.raw = raw;
|
|
52
|
+
this.reason = reason;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {object} PartialExternalRef
|
|
58
|
+
* @property {string} provider
|
|
59
|
+
* @property {string|null} repo github "owner/name" | local repo token | null
|
|
60
|
+
* @property {number|null} issue github only
|
|
61
|
+
* @property {string|null} toCode local only (target feature code)
|
|
62
|
+
* @property {string|null} url url-class only (url + reserved providers)
|
|
63
|
+
* @property {string|null} expect optional expected-state token
|
|
64
|
+
* @property {string|null} note
|
|
65
|
+
* @property {string} raw the citation body (for locatability)
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parse every `xref:` citation in a description cell.
|
|
70
|
+
* @param {string} descriptionCell
|
|
71
|
+
* @returns {{ refs: PartialExternalRef[], errors: ParseError[] }}
|
|
72
|
+
*/
|
|
73
|
+
export function parseCitations(descriptionCell) {
|
|
74
|
+
const refs = [];
|
|
75
|
+
const errors = [];
|
|
76
|
+
if (typeof descriptionCell !== 'string' || descriptionCell.length === 0) {
|
|
77
|
+
return { refs, errors };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
CITATION_RE.lastIndex = 0;
|
|
81
|
+
let m;
|
|
82
|
+
while ((m = CITATION_RE.exec(descriptionCell)) !== null) {
|
|
83
|
+
const raw = m[1];
|
|
84
|
+
try {
|
|
85
|
+
refs.push(parseOne(raw));
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (e instanceof ParseError) errors.push(e);
|
|
88
|
+
else errors.push(new ParseError(raw, String(e && e.message ? e.message : e)));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { refs, errors };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseOne(raw) {
|
|
95
|
+
let rest = raw.trim();
|
|
96
|
+
if (rest.length === 0) throw new ParseError(raw, 'empty xref citation');
|
|
97
|
+
|
|
98
|
+
// Optional trailing options `expect=<tok>` and `note="..."`, order-
|
|
99
|
+
// independent. They are stripped **end-anchored** (must be the trailing
|
|
100
|
+
// whitespace-separated token), so a `note=`/`expect=` substring inside the
|
|
101
|
+
// target itself — e.g. a URL query `https://x/?note=a&expect=b` — is left
|
|
102
|
+
// in the target and never mis-consumed. Each option may appear at most once.
|
|
103
|
+
//
|
|
104
|
+
// Known v1 limitations (faithful to spec §3.1 EBNF, which defines
|
|
105
|
+
// `qstring = DQUOTE *CHAR DQUOTE` with no escape and an HTML-comment
|
|
106
|
+
// carrier): a `note="..."` value cannot contain `"` or the literal `-->`.
|
|
107
|
+
let note = null;
|
|
108
|
+
let expect = null;
|
|
109
|
+
for (let i = 0; i < 2; i++) {
|
|
110
|
+
let m;
|
|
111
|
+
if (note === null && (m = rest.match(/\s+note="([^"]*)"\s*$/))) {
|
|
112
|
+
note = m[1];
|
|
113
|
+
rest = rest.slice(0, m.index).trimEnd();
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (expect === null && (m = rest.match(/\s+expect=(\S+)\s*$/))) {
|
|
117
|
+
expect = m[1];
|
|
118
|
+
rest = rest.slice(0, m.index).trimEnd();
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
// A `note=` option token that exists but was not consumable as a trailing
|
|
124
|
+
// quoted string is a hard parse error (don't silently fold it into target).
|
|
125
|
+
if (note === null && /(^|\s)note=/.test(rest)) {
|
|
126
|
+
if (/(^|\s)note="/.test(rest)) {
|
|
127
|
+
throw new ParseError(raw, 'unterminated or misplaced note="..." (must be a trailing double-quoted token)');
|
|
128
|
+
}
|
|
129
|
+
throw new ParseError(raw, 'note= value must be a double-quoted string');
|
|
130
|
+
}
|
|
131
|
+
// Likewise a stray trailing `expect=` token that wasn't consumed.
|
|
132
|
+
if (expect === null && /(^|\s)expect=\S*\s*$/.test(rest)) {
|
|
133
|
+
throw new ParseError(raw, 'malformed expect= option');
|
|
134
|
+
}
|
|
135
|
+
rest = rest.replace(/\s+/g, ' ').trim();
|
|
136
|
+
|
|
137
|
+
// Remaining: `<provider> <target...>`.
|
|
138
|
+
const firstWs = rest.search(/\s/);
|
|
139
|
+
if (firstWs === -1) {
|
|
140
|
+
throw new ParseError(raw, `missing target after provider "${rest}"`);
|
|
141
|
+
}
|
|
142
|
+
const provider = rest.slice(0, firstWs);
|
|
143
|
+
const target = rest.slice(firstWs + 1).trim();
|
|
144
|
+
|
|
145
|
+
if (!ALL_PROVIDERS.has(provider)) {
|
|
146
|
+
throw new ParseError(
|
|
147
|
+
raw,
|
|
148
|
+
`unknown provider "${provider}" (expected one of ${[...ALL_PROVIDERS].join(', ')})`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (target.length === 0) {
|
|
152
|
+
throw new ParseError(raw, `missing target for provider "${provider}"`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const ref = {
|
|
156
|
+
provider,
|
|
157
|
+
repo: null,
|
|
158
|
+
issue: null,
|
|
159
|
+
toCode: null,
|
|
160
|
+
url: null,
|
|
161
|
+
expect: null,
|
|
162
|
+
note,
|
|
163
|
+
raw,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (provider === 'github') {
|
|
167
|
+
// No `#` in either repo half — `#` delimits the issue (owner/name#issue)
|
|
168
|
+
// and GitHub owners/names cannot contain it. Keeps the citation carrier
|
|
169
|
+
// carrier-equivalent with the feature.json-link writer (XREF_GH_REPO_RE).
|
|
170
|
+
const gh = target.match(/^([^\s/#]+\/[^\s/#]+)#(\d+)$/);
|
|
171
|
+
if (!gh) {
|
|
172
|
+
throw new ParseError(raw, `github target must be "owner/name#issue", got "${target}"`);
|
|
173
|
+
}
|
|
174
|
+
ref.repo = gh[1];
|
|
175
|
+
ref.issue = Number(gh[2]);
|
|
176
|
+
if (expect !== null) {
|
|
177
|
+
if (!GITHUB_EXPECT.has(expect)) {
|
|
178
|
+
throw new ParseError(
|
|
179
|
+
raw, `github expect must be open|closed, got "${expect}"`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
ref.expect = expect;
|
|
183
|
+
}
|
|
184
|
+
} else if (provider === 'local') {
|
|
185
|
+
const parts = target.split(/\s+/);
|
|
186
|
+
if (parts.length !== 2) {
|
|
187
|
+
throw new ParseError(
|
|
188
|
+
raw, `local target must be "<repo> <FEATURE_CODE>", got "${target}"`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const [repoTok, code] = parts;
|
|
192
|
+
// repo token must be a single safe directory name — it is resolved as a
|
|
193
|
+
// sibling dir (path.join(cwd, '..', repoTok)); reject anything with a
|
|
194
|
+
// path separator or traversal so a citation cannot escape the workspace.
|
|
195
|
+
if (!/^[A-Za-z0-9._-]+$/.test(repoTok) || repoTok === '.' || repoTok === '..') {
|
|
196
|
+
throw new ParseError(
|
|
197
|
+
raw,
|
|
198
|
+
`local repo token "${repoTok}" must be a single directory name `
|
|
199
|
+
+ '([A-Za-z0-9._-], no path separators or "."/"..")',
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (!FEATURE_CODE_RE.test(code)) {
|
|
203
|
+
throw new ParseError(raw, `local target feature code "${code}" is not a valid code`);
|
|
204
|
+
}
|
|
205
|
+
ref.repo = repoTok;
|
|
206
|
+
ref.toCode = code;
|
|
207
|
+
if (expect !== null) {
|
|
208
|
+
if (!LOCAL_EXPECT.has(expect)) {
|
|
209
|
+
throw new ParseError(
|
|
210
|
+
raw,
|
|
211
|
+
`local expect must be one of ${[...LOCAL_EXPECT].join('|')}, got "${expect}"`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
ref.expect = expect;
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
// url-class: provider `url` and every reserved provider. The target is a
|
|
218
|
+
// single URL token; `expect=` is syntactically accepted but ignored
|
|
219
|
+
// (these refs are never resolved in v1 — spec §5.2/§9), never a ParseError.
|
|
220
|
+
if (/\s/.test(target)) {
|
|
221
|
+
throw new ParseError(
|
|
222
|
+
raw, `${provider} target must be a single URL, got "${target}"`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (!URI_SCHEME_RE.test(target)) {
|
|
226
|
+
throw new ParseError(
|
|
227
|
+
raw, `${provider} target must be a scheme:// URL, got "${target}"`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
ref.url = target;
|
|
231
|
+
if (expect !== null) ref.expect = expect; // recorded, not validated, not resolved
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return ref;
|
|
235
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartmemory/compose",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40-beta",
|
|
4
4
|
"description": "Structured AI dev pipeline — goal-to-product orchestration with gates, iteration loops, and feature lifecycle management.",
|
|
5
5
|
"author": "SmartMemory",
|
|
6
6
|
"license": "MIT",
|
|
@@ -291,10 +291,11 @@ export async function toolValidateFeature(args = {}) {
|
|
|
291
291
|
|
|
292
292
|
export async function toolValidateProject(args = {}) {
|
|
293
293
|
const { validateProject } = await import('../lib/feature-validator.js');
|
|
294
|
-
const { external_prefixes, feature_json_mode } = args;
|
|
294
|
+
const { external_prefixes, feature_json_mode, external } = args;
|
|
295
295
|
return validateProject(getTargetRoot(), {
|
|
296
296
|
externalPrefixes: external_prefixes,
|
|
297
297
|
featureJsonMode: feature_json_mode,
|
|
298
|
+
external: external === true,
|
|
298
299
|
});
|
|
299
300
|
}
|
|
300
301
|
|
package/server/compose-mcp.js
CHANGED
|
@@ -363,12 +363,13 @@ const TOOLS = [
|
|
|
363
363
|
},
|
|
364
364
|
{
|
|
365
365
|
name: 'validate_project',
|
|
366
|
-
description: 'Run validate_feature for every code in vision-state, ROADMAP, and folders, plus cross-cutting checks (orphan folders, dangling cross-refs, CHANGELOG references, journal index drift). Returns the union of all findings.',
|
|
366
|
+
description: 'Run validate_feature for every code in vision-state, ROADMAP, and folders, plus cross-cutting checks (orphan folders, dangling cross-refs, CHANGELOG references, journal index drift) and read-only external-reference staleness (kind:"external" links + xref: roadmap citations). external:true enables network resolution of github refs (off by default — github refs then emit XREF_RESOLUTION_SKIPPED). Returns the union of all findings.',
|
|
367
367
|
inputSchema: {
|
|
368
368
|
type: 'object',
|
|
369
369
|
properties: {
|
|
370
370
|
external_prefixes: { type: 'array', items: { type: 'string' } },
|
|
371
371
|
feature_json_mode: { type: 'boolean' },
|
|
372
|
+
external: { type: 'boolean', description: 'Resolve github external refs over the network (read-only). Default false: github refs degrade to XREF_RESOLUTION_SKIPPED.' },
|
|
372
373
|
},
|
|
373
374
|
},
|
|
374
375
|
},
|
|
@@ -411,14 +412,19 @@ const TOOLS = [
|
|
|
411
412
|
},
|
|
412
413
|
{
|
|
413
414
|
name: 'link_features',
|
|
414
|
-
description: 'Register a typed cross-feature relationship.
|
|
415
|
+
description: 'Register a typed cross-feature relationship. Two shapes: (1) SAME-PROJECT — kind ∈ surfaced_by|blocks|depends_on|follow_up|supersedes|related, requires to_code; self-links rejected; dedups on (kind,to_code). (2) EXTERNAL (kind:"external") — a cross-project pointer, NOT a same-project link: requires provider; three resolvable sub-shapes — github (repo "owner/name" + integer issue), local (repo token + to_code), url (url); plus reserved url-class providers jira|linear|notion|obsidian (parse-valid, require url, NOT resolved in v1). External dedups on (kind=external, provider, repo, issue|to_code|url). Stores on the source feature; query inverse via get_feature_links(direction:"incoming").',
|
|
415
416
|
inputSchema: {
|
|
416
417
|
type: 'object',
|
|
417
|
-
required: ['from_code', '
|
|
418
|
+
required: ['from_code', 'kind'],
|
|
418
419
|
properties: {
|
|
419
420
|
from_code: { type: 'string' },
|
|
420
|
-
to_code: { type: 'string', description: '
|
|
421
|
-
kind: { type: 'string', enum: ['surfaced_by', 'blocks', 'depends_on', 'follow_up', 'supersedes', 'related'] },
|
|
421
|
+
to_code: { type: 'string', description: 'Same-project: target feature code (required unless kind:"external"). External local: the cited feature code. Need not exist yet.' },
|
|
422
|
+
kind: { type: 'string', enum: ['surfaced_by', 'blocks', 'depends_on', 'follow_up', 'supersedes', 'related', 'external'] },
|
|
423
|
+
provider: { type: 'string', enum: ['github', 'local', 'url', 'jira', 'linear', 'notion', 'obsidian'], description: 'Required when kind:"external". Resolvable: github|local|url. Reserved url-class (require url, not resolved in v1): jira|linear|notion|obsidian.' },
|
|
424
|
+
repo: { type: 'string', description: 'External github: "owner/name". External local: workspace-relative repo token.' },
|
|
425
|
+
issue: { type: 'integer', minimum: 1, description: 'External github: issue/PR number.' },
|
|
426
|
+
url: { type: 'string', description: 'External url-class (url|jira|linear|notion|obsidian): the pointer URL.' },
|
|
427
|
+
expect: { type: 'string', description: 'Optional expected state. github: open|closed. local: a status token. url-class: recorded, never resolved.' },
|
|
422
428
|
note: { type: 'string' },
|
|
423
429
|
force: { type: 'boolean' },
|
|
424
430
|
idempotency_key: { type: 'string' },
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,C as n}from"./App-BOmZUQK9.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-rxaajQx0.js";import{b as i}from"./App-BOmZUQK9.js";import"./chunk-FMBD7UC4-FSbyjfSh.js";import"./chunk-ND2GUHAM-DhD_mt3D.js";import"./chunk-55IACEB6-C074eKNZ.js";import"./chunk-2J33WTMH-DsfEX96t.js";import"./mobile-BwduHUEq.js";import"./index-DHyg44Px.js";import"./graph-uO5hwVZK.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-rxaajQx0.js";import{b as i}from"./App-BOmZUQK9.js";import"./chunk-FMBD7UC4-FSbyjfSh.js";import"./chunk-ND2GUHAM-DhD_mt3D.js";import"./chunk-55IACEB6-C074eKNZ.js";import"./chunk-2J33WTMH-DsfEX96t.js";import"./mobile-BwduHUEq.js";import"./index-DHyg44Px.js";import"./graph-uO5hwVZK.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as r,a as e,s as a,S as s}from"./chunk-AQP2D5EJ-Cg6-5M28.js";import{b as i}from"./App-BOmZUQK9.js";import"./chunk-55IACEB6-C074eKNZ.js";import"./chunk-2J33WTMH-DsfEX96t.js";import"./mobile-BwduHUEq.js";import"./index-DHyg44Px.js";import"./graph-uO5hwVZK.js";var b={parser:a,get db(){return new s(2)},renderer:e,styles:r,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{b as diagram};
|