@tienne/gestalt 0.99.0 → 0.101.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/CLAUDE.md +6 -1
- package/dist/package.json +5 -3
- package/dist/plugin/research-sources/amplitude/SOURCE.md +28 -0
- package/dist/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
- package/dist/plugin/research-sources/app-store-search/SOURCE.md +25 -0
- package/dist/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
- package/dist/plugin/research-sources/uibowl/SOURCE.md +26 -0
- package/dist/plugin/research-sources/web-search/SOURCE.md +24 -0
- package/dist/plugin/skills/_shared/agent-delegation.md +30 -7
- package/dist/plugin/skills/_shared/agent-model.md +3 -1
- package/dist/plugin/skills/_shared/proactive-routing.md +5 -0
- package/dist/plugin/skills/brief/SKILL.md +5 -7
- package/dist/plugin/skills/explain/SKILL.md +4 -5
- package/dist/plugin/skills/jira-create/SKILL.md +3 -4
- package/dist/plugin/skills/market-scan/SKILL.md +61 -0
- package/dist/plugin/skills/pr/SKILL.md +4 -6
- package/dist/plugin/skills/presentation/SKILL.md +18 -16
- package/dist/plugin/skills/research/SKILL.md +438 -0
- package/dist/plugin/skills/research/references/design.md +938 -0
- package/dist/plugin/skills/research/references/output-template.md +366 -0
- package/dist/plugin/skills/research/scripts/pii-vision.swift +327 -0
- package/dist/plugin/skills/research/scripts/slide-extract.mjs +223 -0
- package/dist/plugin/skills/reverse-plan/SKILL.md +52 -0
- package/dist/plugin/skills/review/SKILL.md +20 -25
- package/dist/plugin/skills/review-reply/SKILL.md +4 -5
- package/dist/plugin/skills/screen-ref/SKILL.md +63 -0
- package/dist/plugin/skills/slack-send/SKILL.md +3 -4
- package/dist/plugin/skills/ux-research/SKILL.md +51 -0
- package/dist/src/core/config.d.ts +3 -0
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +4 -0
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +93 -0
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +110 -0
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +11 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/research-passthrough.d.ts +21 -0
- package/dist/src/mcp/tools/research-passthrough.d.ts.map +1 -0
- package/dist/src/mcp/tools/research-passthrough.js +566 -0
- package/dist/src/mcp/tools/research-passthrough.js.map +1 -0
- package/dist/src/research/canvas-view.d.ts +5 -0
- package/dist/src/research/canvas-view.d.ts.map +1 -0
- package/dist/src/research/canvas-view.js +274 -0
- package/dist/src/research/canvas-view.js.map +1 -0
- package/dist/src/research/evidence.d.ts +145 -0
- package/dist/src/research/evidence.d.ts.map +1 -0
- package/dist/src/research/evidence.js +50 -0
- package/dist/src/research/evidence.js.map +1 -0
- package/dist/src/research/figma-plan.d.ts +80 -0
- package/dist/src/research/figma-plan.d.ts.map +1 -0
- package/dist/src/research/figma-plan.js +527 -0
- package/dist/src/research/figma-plan.js.map +1 -0
- package/dist/src/research/figma-script.d.ts +19 -0
- package/dist/src/research/figma-script.d.ts.map +1 -0
- package/dist/src/research/figma-script.js +363 -0
- package/dist/src/research/figma-script.js.map +1 -0
- package/dist/src/research/index.d.ts +8 -0
- package/dist/src/research/index.d.ts.map +1 -0
- package/dist/src/research/index.js +8 -0
- package/dist/src/research/index.js.map +1 -0
- package/dist/src/research/pii-core.d.ts +16 -0
- package/dist/src/research/pii-core.d.ts.map +1 -0
- package/dist/src/research/pii-core.js +345 -0
- package/dist/src/research/pii-core.js.map +1 -0
- package/dist/src/research/pii-dom.d.ts +13 -0
- package/dist/src/research/pii-dom.d.ts.map +1 -0
- package/dist/src/research/pii-dom.js +208 -0
- package/dist/src/research/pii-dom.js.map +1 -0
- package/dist/src/research/pii-image.d.ts +71 -0
- package/dist/src/research/pii-image.d.ts.map +1 -0
- package/dist/src/research/pii-image.js +163 -0
- package/dist/src/research/pii-image.js.map +1 -0
- package/dist/src/research/pii.d.ts +21 -0
- package/dist/src/research/pii.d.ts.map +1 -0
- package/dist/src/research/pii.js +47 -0
- package/dist/src/research/pii.js.map +1 -0
- package/dist/src/research/product-profile.d.ts +108 -0
- package/dist/src/research/product-profile.d.ts.map +1 -0
- package/dist/src/research/product-profile.js +104 -0
- package/dist/src/research/product-profile.js.map +1 -0
- package/dist/src/research/render-html.d.ts +203 -0
- package/dist/src/research/render-html.d.ts.map +1 -0
- package/dist/src/research/render-html.js +901 -0
- package/dist/src/research/render-html.js.map +1 -0
- package/dist/src/research/schema-base.d.ts +5 -0
- package/dist/src/research/schema-base.d.ts.map +1 -0
- package/dist/src/research/schema-base.js +10 -0
- package/dist/src/research/schema-base.js.map +1 -0
- package/dist/src/research/schema.d.ts +5142 -0
- package/dist/src/research/schema.d.ts.map +1 -0
- package/dist/src/research/schema.js +991 -0
- package/dist/src/research/schema.js.map +1 -0
- package/dist/src/research/source-create.d.ts +79 -0
- package/dist/src/research/source-create.d.ts.map +1 -0
- package/dist/src/research/source-create.js +226 -0
- package/dist/src/research/source-create.js.map +1 -0
- package/dist/src/research/source-registry.d.ts +29 -0
- package/dist/src/research/source-registry.d.ts.map +1 -0
- package/dist/src/research/source-registry.js +74 -0
- package/dist/src/research/source-registry.js.map +1 -0
- package/dist/src/research/source-schema.d.ts +158 -0
- package/dist/src/research/source-schema.d.ts.map +1 -0
- package/dist/src/research/source-schema.js +215 -0
- package/dist/src/research/source-schema.js.map +1 -0
- package/dist/src/research/source-status.d.ts +40 -0
- package/dist/src/research/source-status.d.ts.map +1 -0
- package/dist/src/research/source-status.js +109 -0
- package/dist/src/research/source-status.js.map +1 -0
- package/dist/src/research/store.d.ts +24 -0
- package/dist/src/research/store.d.ts.map +1 -0
- package/dist/src/research/store.js +60 -0
- package/dist/src/research/store.js.map +1 -0
- package/dist/src/research/target-view.d.ts +33 -0
- package/dist/src/research/target-view.d.ts.map +1 -0
- package/dist/src/research/target-view.js +133 -0
- package/dist/src/research/target-view.js.map +1 -0
- package/dist/src/research/target.d.ts +883 -0
- package/dist/src/research/target.d.ts.map +1 -0
- package/dist/src/research/target.js +323 -0
- package/dist/src/research/target.js.map +1 -0
- package/dist/src/research/video-transfer.d.ts +28 -0
- package/dist/src/research/video-transfer.d.ts.map +1 -0
- package/dist/src/research/video-transfer.js +100 -0
- package/dist/src/research/video-transfer.js.map +1 -0
- package/package.json +5 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/research-sources/amplitude/SOURCE.md +28 -0
- package/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
- package/plugin/research-sources/app-store-search/SOURCE.md +25 -0
- package/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
- package/plugin/research-sources/uibowl/SOURCE.md +26 -0
- package/plugin/research-sources/web-search/SOURCE.md +24 -0
- package/plugin/skills/_shared/agent-delegation.md +30 -7
- package/plugin/skills/_shared/agent-model.md +3 -1
- package/plugin/skills/_shared/proactive-routing.md +5 -0
- package/plugin/skills/brief/SKILL.md +5 -7
- package/plugin/skills/explain/SKILL.md +4 -5
- package/plugin/skills/jira-create/SKILL.md +3 -4
- package/plugin/skills/market-scan/SKILL.md +61 -0
- package/plugin/skills/pr/SKILL.md +4 -6
- package/plugin/skills/presentation/SKILL.md +18 -16
- package/plugin/skills/research/SKILL.md +438 -0
- package/plugin/skills/research/references/design.md +938 -0
- package/plugin/skills/research/references/output-template.md +366 -0
- package/plugin/skills/research/scripts/pii-vision.swift +327 -0
- package/plugin/skills/research/scripts/slide-extract.mjs +223 -0
- package/plugin/skills/reverse-plan/SKILL.md +52 -0
- package/plugin/skills/review/SKILL.md +20 -25
- package/plugin/skills/review-reply/SKILL.md +4 -5
- package/plugin/skills/screen-ref/SKILL.md +63 -0
- package/plugin/skills/slack-send/SKILL.md +3 -4
- package/plugin/skills/ux-research/SKILL.md +51 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ValidationError } from '../core/errors.js';
|
|
3
|
+
import { type Result } from '../core/result.js';
|
|
4
|
+
export declare const SOURCE_STAGES: readonly ["product-discovery", "scale", "screen-map", "evidence", "baseline"];
|
|
5
|
+
export declare const SOURCE_ACCESS_KINDS: readonly ["mcp", "browser", "http", "env"];
|
|
6
|
+
export declare const SOURCE_EVIDENCE: readonly ["observation", "company-statement", "interpretation", "estimate"];
|
|
7
|
+
export declare const SOURCE_AXES: readonly ["same-market", "same-problem"];
|
|
8
|
+
export type SourceStage = (typeof SOURCE_STAGES)[number];
|
|
9
|
+
export type SourceAccessKind = (typeof SOURCE_ACCESS_KINDS)[number];
|
|
10
|
+
/** 파일 하나의 크기 상한. 개인 디렉토리라도 통째로 읽으므로 막아둔다 */
|
|
11
|
+
export declare const SOURCE_MAX_BYTES: number;
|
|
12
|
+
/** 사설 주소로 가는 길을 막는다. 숫자 주소와 점 없는 이름은 정규식에서 이미 걸러진다 */
|
|
13
|
+
export declare function isPublicHostname(host: string): boolean;
|
|
14
|
+
/** 비밀번호나 키가 SOURCE.md에 들어가는 걸 막는다. 환경변수 이름만 적는다 */
|
|
15
|
+
export declare function findSecret(text: string): string | undefined;
|
|
16
|
+
export declare const sourceFrontmatterSchema: z.ZodEffects<z.ZodObject<{
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
description: z.ZodString;
|
|
19
|
+
stage: z.ZodEffects<z.ZodArray<z.ZodEnum<["product-discovery", "scale", "screen-map", "evidence", "baseline"]>, "many">, ("evidence" | "product-discovery" | "scale" | "screen-map" | "baseline")[], unknown>;
|
|
20
|
+
axis: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodEnum<["same-market", "same-problem"]>, "many">>, ("same-market" | "same-problem")[], unknown>;
|
|
21
|
+
access: z.ZodArray<z.ZodEffects<z.ZodUnknown, {
|
|
22
|
+
server?: string | undefined;
|
|
23
|
+
tools?: string[] | undefined;
|
|
24
|
+
kind: "mcp";
|
|
25
|
+
name?: undefined;
|
|
26
|
+
} | {
|
|
27
|
+
site: string;
|
|
28
|
+
login?: "none" | "human" | undefined;
|
|
29
|
+
kind: "browser";
|
|
30
|
+
name?: undefined;
|
|
31
|
+
} | {
|
|
32
|
+
host: string;
|
|
33
|
+
probe?: string | undefined;
|
|
34
|
+
kind: "http";
|
|
35
|
+
name?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "env";
|
|
38
|
+
name: string;
|
|
39
|
+
}, unknown>, "many">;
|
|
40
|
+
requires: z.ZodDefault<z.ZodObject<{
|
|
41
|
+
mcpServer: z.ZodOptional<z.ZodString>;
|
|
42
|
+
env: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], unknown>;
|
|
43
|
+
browserLogin: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
|
|
44
|
+
}, "strict", z.ZodTypeAny, {
|
|
45
|
+
env: string[];
|
|
46
|
+
mcpServer?: string | undefined;
|
|
47
|
+
browserLogin?: string | undefined;
|
|
48
|
+
}, {
|
|
49
|
+
env?: unknown;
|
|
50
|
+
mcpServer?: string | undefined;
|
|
51
|
+
browserLogin?: string | undefined;
|
|
52
|
+
}>>;
|
|
53
|
+
/** estimate는 제3자 추정치라 근거 상태에서는 해석으로 받는다 */
|
|
54
|
+
evidence: z.ZodEffects<z.ZodDefault<z.ZodEnum<["observation", "company-statement", "interpretation", "estimate"]>>, "observation" | "company-statement" | "interpretation", "observation" | "company-statement" | "interpretation" | "estimate" | undefined>;
|
|
55
|
+
limits: z.ZodOptional<z.ZodString>;
|
|
56
|
+
/** 이 출처를 못 쓸 때 대신 쓰는 방법. 0번 전제의 근거 한계 문장에 들어간다 */
|
|
57
|
+
fallback: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, "strip", z.ZodTypeAny, {
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
requires: {
|
|
62
|
+
env: string[];
|
|
63
|
+
mcpServer?: string | undefined;
|
|
64
|
+
browserLogin?: string | undefined;
|
|
65
|
+
};
|
|
66
|
+
stage: ("evidence" | "product-discovery" | "scale" | "screen-map" | "baseline")[];
|
|
67
|
+
evidence: "observation" | "company-statement" | "interpretation";
|
|
68
|
+
axis: ("same-market" | "same-problem")[];
|
|
69
|
+
access: ({
|
|
70
|
+
server?: string | undefined;
|
|
71
|
+
tools?: string[] | undefined;
|
|
72
|
+
kind: "mcp";
|
|
73
|
+
name?: undefined;
|
|
74
|
+
} | {
|
|
75
|
+
site: string;
|
|
76
|
+
login?: "none" | "human" | undefined;
|
|
77
|
+
kind: "browser";
|
|
78
|
+
name?: undefined;
|
|
79
|
+
} | {
|
|
80
|
+
host: string;
|
|
81
|
+
probe?: string | undefined;
|
|
82
|
+
kind: "http";
|
|
83
|
+
name?: undefined;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "env";
|
|
86
|
+
name: string;
|
|
87
|
+
})[];
|
|
88
|
+
limits?: string | undefined;
|
|
89
|
+
fallback?: string | undefined;
|
|
90
|
+
}, {
|
|
91
|
+
name: string;
|
|
92
|
+
description: string;
|
|
93
|
+
access: unknown[];
|
|
94
|
+
requires?: {
|
|
95
|
+
env?: unknown;
|
|
96
|
+
mcpServer?: string | undefined;
|
|
97
|
+
browserLogin?: string | undefined;
|
|
98
|
+
} | undefined;
|
|
99
|
+
stage?: unknown;
|
|
100
|
+
evidence?: "observation" | "company-statement" | "interpretation" | "estimate" | undefined;
|
|
101
|
+
axis?: unknown;
|
|
102
|
+
limits?: string | undefined;
|
|
103
|
+
fallback?: string | undefined;
|
|
104
|
+
}>, {
|
|
105
|
+
name: string;
|
|
106
|
+
description: string;
|
|
107
|
+
requires: {
|
|
108
|
+
env: string[];
|
|
109
|
+
mcpServer?: string | undefined;
|
|
110
|
+
browserLogin?: string | undefined;
|
|
111
|
+
};
|
|
112
|
+
stage: ("evidence" | "product-discovery" | "scale" | "screen-map" | "baseline")[];
|
|
113
|
+
evidence: "observation" | "company-statement" | "interpretation";
|
|
114
|
+
axis: ("same-market" | "same-problem")[];
|
|
115
|
+
access: ({
|
|
116
|
+
server?: string | undefined;
|
|
117
|
+
tools?: string[] | undefined;
|
|
118
|
+
kind: "mcp";
|
|
119
|
+
name?: undefined;
|
|
120
|
+
} | {
|
|
121
|
+
site: string;
|
|
122
|
+
login?: "none" | "human" | undefined;
|
|
123
|
+
kind: "browser";
|
|
124
|
+
name?: undefined;
|
|
125
|
+
} | {
|
|
126
|
+
host: string;
|
|
127
|
+
probe?: string | undefined;
|
|
128
|
+
kind: "http";
|
|
129
|
+
name?: undefined;
|
|
130
|
+
} | {
|
|
131
|
+
kind: "env";
|
|
132
|
+
name: string;
|
|
133
|
+
})[];
|
|
134
|
+
limits?: string | undefined;
|
|
135
|
+
fallback?: string | undefined;
|
|
136
|
+
}, {
|
|
137
|
+
name: string;
|
|
138
|
+
description: string;
|
|
139
|
+
access: unknown[];
|
|
140
|
+
requires?: {
|
|
141
|
+
env?: unknown;
|
|
142
|
+
mcpServer?: string | undefined;
|
|
143
|
+
browserLogin?: string | undefined;
|
|
144
|
+
} | undefined;
|
|
145
|
+
stage?: unknown;
|
|
146
|
+
evidence?: "observation" | "company-statement" | "interpretation" | "estimate" | undefined;
|
|
147
|
+
axis?: unknown;
|
|
148
|
+
limits?: string | undefined;
|
|
149
|
+
fallback?: string | undefined;
|
|
150
|
+
}>;
|
|
151
|
+
export type SourceFrontmatter = z.infer<typeof sourceFrontmatterSchema>;
|
|
152
|
+
export type SourceAccess = SourceFrontmatter['access'][number];
|
|
153
|
+
export interface SourceDefinition {
|
|
154
|
+
frontmatter: SourceFrontmatter;
|
|
155
|
+
body: string;
|
|
156
|
+
}
|
|
157
|
+
export declare function parseSourceMd(content: string): Result<SourceDefinition, ValidationError>;
|
|
158
|
+
//# sourceMappingURL=source-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-schema.d.ts","sourceRoot":"","sources":["../../../src/research/source-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzD,eAAO,MAAM,aAAa,+EAMhB,CAAC;AACX,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAC9E,eAAO,MAAM,eAAe,6EAKlB,CAAC;AACX,eAAO,MAAM,WAAW,0CAA2C,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,QAAY,CAAC;AAkB1C,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAgBD,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK3D;AA4ED,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAehC,2CAA2C;;;IAM3C,kDAAkD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBlD,CAAC;AAEL,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAgBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,eAAe,CAAC,CA0CxF"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import matter from 'gray-matter';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ValidationError } from '../core/errors.js';
|
|
4
|
+
import { err, ok } from '../core/result.js';
|
|
5
|
+
export const SOURCE_STAGES = [
|
|
6
|
+
'product-discovery',
|
|
7
|
+
'scale',
|
|
8
|
+
'screen-map',
|
|
9
|
+
'evidence',
|
|
10
|
+
'baseline',
|
|
11
|
+
];
|
|
12
|
+
export const SOURCE_ACCESS_KINDS = ['mcp', 'browser', 'http', 'env'];
|
|
13
|
+
export const SOURCE_EVIDENCE = [
|
|
14
|
+
'observation',
|
|
15
|
+
'company-statement',
|
|
16
|
+
'interpretation',
|
|
17
|
+
'estimate',
|
|
18
|
+
];
|
|
19
|
+
export const SOURCE_AXES = ['same-market', 'same-problem'];
|
|
20
|
+
/** 파일 하나의 크기 상한. 개인 디렉토리라도 통째로 읽으므로 막아둔다 */
|
|
21
|
+
export const SOURCE_MAX_BYTES = 64 * 1024;
|
|
22
|
+
const NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
23
|
+
const ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
24
|
+
/** `claude.ai Amplitude`처럼 커넥터 이름에 공백이 들어간다. 연속 공백과 끝 공백은 막는다 */
|
|
25
|
+
const MCP_SERVER_PATTERN = /^(?!.* {2})(?!.* $)[A-Za-z0-9][A-Za-z0-9_.: -]{0,63}$/;
|
|
26
|
+
const MCP_TOOL_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
27
|
+
const HOST_PATTERN = /^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/;
|
|
28
|
+
const PRIVATE_HOST_SUFFIXES = [
|
|
29
|
+
'.local',
|
|
30
|
+
'.internal',
|
|
31
|
+
'.localhost',
|
|
32
|
+
'.lan',
|
|
33
|
+
'.corp',
|
|
34
|
+
'.home',
|
|
35
|
+
'.intranet',
|
|
36
|
+
];
|
|
37
|
+
/** 사설 주소로 가는 길을 막는다. 숫자 주소와 점 없는 이름은 정규식에서 이미 걸러진다 */
|
|
38
|
+
export function isPublicHostname(host) {
|
|
39
|
+
const h = host.toLowerCase();
|
|
40
|
+
return HOST_PATTERN.test(h) && !PRIVATE_HOST_SUFFIXES.some((s) => h.endsWith(s));
|
|
41
|
+
}
|
|
42
|
+
const SECRET_PATTERNS = [
|
|
43
|
+
[/\b(?:sk|pk|rk)-[A-Za-z0-9_-]{16,}/, 'API 키로 보이는 문자열'],
|
|
44
|
+
[/\b(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]{16,}/, 'GitHub 토큰으로 보이는 문자열'],
|
|
45
|
+
[/\bxox[abprs]-[A-Za-z0-9-]{10,}/, 'Slack 토큰으로 보이는 문자열'],
|
|
46
|
+
[/\bAKIA[0-9A-Z]{12,}/, 'AWS 키로 보이는 문자열'],
|
|
47
|
+
[/\bAIza[0-9A-Za-z_-]{20,}/, 'Google API 키로 보이는 문자열'],
|
|
48
|
+
[/\bBearer\s+[A-Za-z0-9._~+/-]{16,}/i, 'Bearer 토큰'],
|
|
49
|
+
[/https?:\/\/[^\s/:@]+:[^\s/@]+@/, '주소에 박힌 계정과 비밀번호'],
|
|
50
|
+
[
|
|
51
|
+
/(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*["']?[A-Za-z0-9+/=._-]{12,}/i,
|
|
52
|
+
'비밀번호나 키 값',
|
|
53
|
+
],
|
|
54
|
+
];
|
|
55
|
+
/** 비밀번호나 키가 SOURCE.md에 들어가는 걸 막는다. 환경변수 이름만 적는다 */
|
|
56
|
+
export function findSecret(text) {
|
|
57
|
+
for (const [pattern, label] of SECRET_PATTERNS) {
|
|
58
|
+
if (pattern.test(text))
|
|
59
|
+
return label;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const hostSchema = z
|
|
64
|
+
.string()
|
|
65
|
+
.transform((s) => s.toLowerCase())
|
|
66
|
+
.refine(isPublicHostname, '공개 호스트 이름만 쓴다 (IP 주소, localhost, 사내 이름 불가)');
|
|
67
|
+
const accessEntrySchema = z.unknown().transform((raw, ctx) => {
|
|
68
|
+
const fail = (message) => {
|
|
69
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message });
|
|
70
|
+
return z.NEVER;
|
|
71
|
+
};
|
|
72
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
73
|
+
return fail('access 항목은 `mcp: {...}` 같은 한 키짜리 객체다');
|
|
74
|
+
}
|
|
75
|
+
const keys = Object.keys(raw);
|
|
76
|
+
const kind = keys[0];
|
|
77
|
+
if (keys.length !== 1 || !kind || !SOURCE_ACCESS_KINDS.includes(kind)) {
|
|
78
|
+
return fail(`access 방법은 ${SOURCE_ACCESS_KINDS.join(', ')}만 쓴다 (받은 키: ${keys.join(', ') || '없음'})`);
|
|
79
|
+
}
|
|
80
|
+
const value = raw[kind];
|
|
81
|
+
const parse = (schema) => {
|
|
82
|
+
const r = schema.safeParse(value);
|
|
83
|
+
if (r.success)
|
|
84
|
+
return r.data;
|
|
85
|
+
for (const issue of r.error.issues) {
|
|
86
|
+
ctx.addIssue({ ...issue, path: [kind, ...issue.path] });
|
|
87
|
+
}
|
|
88
|
+
return z.NEVER;
|
|
89
|
+
};
|
|
90
|
+
switch (kind) {
|
|
91
|
+
case 'mcp':
|
|
92
|
+
return {
|
|
93
|
+
kind: 'mcp',
|
|
94
|
+
...parse(z
|
|
95
|
+
.object({
|
|
96
|
+
server: z
|
|
97
|
+
.string()
|
|
98
|
+
.regex(MCP_SERVER_PATTERN, 'MCP 서버 이름 형식이 아니다')
|
|
99
|
+
.optional(),
|
|
100
|
+
/** 서버 이름은 사람마다 달라서 도구 이름으로 알아본다. 하나라도 세션에 있으면 연결된 걸로 본다 */
|
|
101
|
+
tools: z
|
|
102
|
+
.array(z.string().regex(MCP_TOOL_PATTERN, 'MCP 도구 이름 형식이 아니다'))
|
|
103
|
+
.min(1)
|
|
104
|
+
.optional(),
|
|
105
|
+
})
|
|
106
|
+
.strict()
|
|
107
|
+
.refine((m) => m.server || m.tools, 'MCP는 server나 tools 중 하나는 적는다')),
|
|
108
|
+
};
|
|
109
|
+
case 'browser':
|
|
110
|
+
return {
|
|
111
|
+
kind: 'browser',
|
|
112
|
+
...parse(z
|
|
113
|
+
.object({ site: hostSchema, login: z.enum(['human', 'none']).default('human') })
|
|
114
|
+
.strict()),
|
|
115
|
+
};
|
|
116
|
+
case 'http':
|
|
117
|
+
return {
|
|
118
|
+
kind: 'http',
|
|
119
|
+
...parse(z.object({ host: hostSchema, probe: z.string().url().optional() }).strict()),
|
|
120
|
+
};
|
|
121
|
+
default:
|
|
122
|
+
return {
|
|
123
|
+
kind: 'env',
|
|
124
|
+
name: parse(z.string().regex(ENV_NAME_PATTERN, '환경변수 이름은 대문자와 _만 쓴다')),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const toArray = (v) => (v === undefined || Array.isArray(v) ? v : [v]);
|
|
129
|
+
export const sourceFrontmatterSchema = z
|
|
130
|
+
.object({
|
|
131
|
+
name: z.string().regex(NAME_PATTERN, '이름은 소문자, 숫자, -만 쓴다 (40자까지)'),
|
|
132
|
+
description: z.string().min(1),
|
|
133
|
+
stage: z.preprocess(toArray, z.array(z.enum(SOURCE_STAGES)).min(1)),
|
|
134
|
+
axis: z.preprocess(toArray, z.array(z.enum(SOURCE_AXES)).default([])),
|
|
135
|
+
access: z.array(accessEntrySchema).min(1),
|
|
136
|
+
requires: z
|
|
137
|
+
.object({
|
|
138
|
+
mcpServer: z.string().regex(MCP_SERVER_PATTERN).optional(),
|
|
139
|
+
env: z.preprocess(toArray, z.array(z.string().regex(ENV_NAME_PATTERN)).default([])),
|
|
140
|
+
browserLogin: hostSchema.optional(),
|
|
141
|
+
})
|
|
142
|
+
.strict()
|
|
143
|
+
.default({}),
|
|
144
|
+
/** estimate는 제3자 추정치라 근거 상태에서는 해석으로 받는다 */
|
|
145
|
+
evidence: z
|
|
146
|
+
.enum(SOURCE_EVIDENCE)
|
|
147
|
+
.default('observation')
|
|
148
|
+
.transform((e) => (e === 'estimate' ? 'interpretation' : e)),
|
|
149
|
+
limits: z.string().optional(),
|
|
150
|
+
/** 이 출처를 못 쓸 때 대신 쓰는 방법. 0번 전제의 근거 한계 문장에 들어간다 */
|
|
151
|
+
fallback: z.string().optional(),
|
|
152
|
+
})
|
|
153
|
+
.superRefine((fm, ctx) => {
|
|
154
|
+
fm.access.forEach((a, i) => {
|
|
155
|
+
if (a.kind !== 'http' || !a.probe)
|
|
156
|
+
return;
|
|
157
|
+
let url;
|
|
158
|
+
try {
|
|
159
|
+
url = new URL(a.probe);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const hostOk = url.hostname === a.host || url.hostname.endsWith(`.${a.host}`);
|
|
165
|
+
if (url.protocol !== 'https:' || url.username || url.password || !hostOk) {
|
|
166
|
+
ctx.addIssue({
|
|
167
|
+
code: z.ZodIssueCode.custom,
|
|
168
|
+
path: ['access', i, 'http', 'probe'],
|
|
169
|
+
message: 'probe는 같은 호스트의 https 주소만 쓴다 (계정 정보 불가)',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
const REQUIRED_SECTIONS = ['찾는 법', '멈추는 곳'];
|
|
175
|
+
/** gray-matter의 js 엔진은 frontmatter를 코드로 실행한다. 개인 디렉토리 파일이라도 막는다 */
|
|
176
|
+
const SAFE_MATTER_OPTIONS = {
|
|
177
|
+
engines: {
|
|
178
|
+
js: () => {
|
|
179
|
+
throw new Error('js frontmatter는 쓰지 않는다');
|
|
180
|
+
},
|
|
181
|
+
javascript: () => {
|
|
182
|
+
throw new Error('js frontmatter는 쓰지 않는다');
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
export function parseSourceMd(content) {
|
|
187
|
+
if (Buffer.byteLength(content, 'utf-8') > SOURCE_MAX_BYTES) {
|
|
188
|
+
return err(new ValidationError('SOURCE.md가 너무 크다 (64KB 상한)', []));
|
|
189
|
+
}
|
|
190
|
+
const secret = findSecret(content);
|
|
191
|
+
if (secret) {
|
|
192
|
+
return err(new ValidationError(`SOURCE.md에 ${secret}이 들어 있다. 비밀번호와 키는 적지 않고 환경변수 이름이나 MCP 서버 이름만 적는다`, [secret]));
|
|
193
|
+
}
|
|
194
|
+
let data;
|
|
195
|
+
let body;
|
|
196
|
+
try {
|
|
197
|
+
const parsed = matter(content, SAFE_MATTER_OPTIONS);
|
|
198
|
+
data = parsed.data;
|
|
199
|
+
body = parsed.content.trim();
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
return err(new ValidationError(`frontmatter를 읽지 못했다: ${e instanceof Error ? e.message : String(e)}`, []));
|
|
203
|
+
}
|
|
204
|
+
const fm = sourceFrontmatterSchema.safeParse(data);
|
|
205
|
+
if (!fm.success) {
|
|
206
|
+
const issues = fm.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`);
|
|
207
|
+
return err(new ValidationError(`SOURCE.md frontmatter 오류: ${issues.join('; ')}`, issues));
|
|
208
|
+
}
|
|
209
|
+
const missing = REQUIRED_SECTIONS.filter((s) => !new RegExp(`^##\\s+${s}\\s*$`, 'm').test(body));
|
|
210
|
+
if (missing.length > 0) {
|
|
211
|
+
return err(new ValidationError(`본문에 절이 빠졌다: ${missing.map((s) => `## ${s}`).join(', ')}`, missing));
|
|
212
|
+
}
|
|
213
|
+
return ok({ frontmatter: fm.data, body });
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=source-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-schema.js","sourceRoot":"","sources":["../../../src/research/source-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { LoadedSource } from './source-registry.js';
|
|
2
|
+
import type { SourceAccess, SourceStage } from './source-schema.js';
|
|
3
|
+
/** 같은 세션의 다른 MCP 연결과 브라우저 로그인은 ges 서버가 못 본다. 세션이 알려준다 */
|
|
4
|
+
export interface SessionAvailability {
|
|
5
|
+
mcpServers?: string[];
|
|
6
|
+
/** 세션의 MCP 도구 이름(`mcp__<서버>__<도구>`). 서버 이름이 사람마다 달라서 도구 이름으로 출처를 알아본다 */
|
|
7
|
+
mcpTools?: string[];
|
|
8
|
+
browserLogins?: string[];
|
|
9
|
+
}
|
|
10
|
+
export type SourceStatus = 'usable' | 'fallback' | 'skipped';
|
|
11
|
+
export interface SourceEvaluation {
|
|
12
|
+
status: SourceStatus;
|
|
13
|
+
/** 실제로 쓰게 될 접근 방법. 건너뛰면 없다 */
|
|
14
|
+
via?: SourceAccess['kind'];
|
|
15
|
+
/** MCP로 쓸 때 이 세션에서 부를 서버. 도구 이름으로 찾았으면 그 도구의 접두어에서 뽑는다 */
|
|
16
|
+
mcpServer?: string;
|
|
17
|
+
note: string;
|
|
18
|
+
}
|
|
19
|
+
/** access를 위에서부터 시도해 첫 번째로 되는 방법을 고른다 */
|
|
20
|
+
export declare function evaluateSource(source: LoadedSource, avail: SessionAvailability, env?: NodeJS.ProcessEnv): SourceEvaluation;
|
|
21
|
+
export interface SourceReportRow {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
stage: SourceStage[];
|
|
25
|
+
origin: LoadedSource['origin'];
|
|
26
|
+
overridden: boolean;
|
|
27
|
+
evidence: LoadedSource['frontmatter']['evidence'];
|
|
28
|
+
status: SourceStatus;
|
|
29
|
+
via?: SourceAccess['kind'];
|
|
30
|
+
mcpServer?: string;
|
|
31
|
+
note: string;
|
|
32
|
+
limits?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SourceReport {
|
|
35
|
+
sources: SourceReportRow[];
|
|
36
|
+
/** 0번 전제의 근거 한계에 그대로 옮겨 적는 문장 */
|
|
37
|
+
evidenceLimits: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function buildSourceReport(sources: LoadedSource[], avail: SessionAvailability, env?: NodeJS.ProcessEnv, stageFilter?: SourceStage): SourceReport;
|
|
40
|
+
//# sourceMappingURL=source-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-status.d.ts","sourceRoot":"","sources":["../../../src/research/source-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEpE,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AA6DD,yCAAyC;AACzC,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,mBAAmB,EAC1B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,gBAAgB,CAwBlB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,iCAAiC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EAAE,EACvB,KAAK,EAAE,mBAAmB,EAC1B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,WAAW,CAAC,EAAE,WAAW,GACxB,YAAY,CA+Bd"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** 세션에서 이 MCP 출처를 맡는 서버. 없으면 undefined */
|
|
2
|
+
function findMcpServer(access, avail) {
|
|
3
|
+
if (access.server && avail.mcpServers?.includes(access.server))
|
|
4
|
+
return access.server;
|
|
5
|
+
for (const full of avail.mcpTools ?? []) {
|
|
6
|
+
const m = /^mcp__(.+)__([^_].*)$/.exec(full);
|
|
7
|
+
const [server, tool] = m ? [m[1], m[2]] : [undefined, full];
|
|
8
|
+
if (access.tools?.includes(tool))
|
|
9
|
+
return server ?? access.server ?? '';
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const mcpLabel = (access) => access.server ?? access.tools?.join(', ') ?? '';
|
|
14
|
+
const VIA_LABEL = {
|
|
15
|
+
mcp: 'MCP',
|
|
16
|
+
browser: '브라우저',
|
|
17
|
+
http: '공개 API',
|
|
18
|
+
env: 'API 키',
|
|
19
|
+
};
|
|
20
|
+
function unmetReason(access, avail, env) {
|
|
21
|
+
switch (access.kind) {
|
|
22
|
+
case 'mcp':
|
|
23
|
+
return findMcpServer(access, avail) === undefined
|
|
24
|
+
? `MCP(${mcpLabel(access)}) 미연결`
|
|
25
|
+
: undefined;
|
|
26
|
+
case 'browser':
|
|
27
|
+
if (access.login === 'none')
|
|
28
|
+
return undefined;
|
|
29
|
+
return avail.browserLogins?.includes(access.site) ? undefined : `${access.site} 로그인 안 됨`;
|
|
30
|
+
case 'http':
|
|
31
|
+
return undefined;
|
|
32
|
+
case 'env':
|
|
33
|
+
return env[access.name] ? undefined : `환경변수 ${access.name} 없음`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function unmetRequirement(source, avail, env) {
|
|
37
|
+
const req = source.frontmatter.requires;
|
|
38
|
+
if (req.mcpServer && !avail.mcpServers?.includes(req.mcpServer)) {
|
|
39
|
+
return `MCP(${req.mcpServer}) 미연결`;
|
|
40
|
+
}
|
|
41
|
+
const missingEnv = req.env.find((name) => !env[name]);
|
|
42
|
+
if (missingEnv)
|
|
43
|
+
return `환경변수 ${missingEnv} 없음`;
|
|
44
|
+
if (req.browserLogin && !avail.browserLogins?.includes(req.browserLogin)) {
|
|
45
|
+
return `${req.browserLogin} 로그인 안 됨`;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/** access를 위에서부터 시도해 첫 번째로 되는 방법을 고른다 */
|
|
50
|
+
export function evaluateSource(source, avail, env = process.env) {
|
|
51
|
+
const requirement = unmetRequirement(source, avail, env);
|
|
52
|
+
if (requirement)
|
|
53
|
+
return { status: 'skipped', note: `${requirement}이라 건너뜀` };
|
|
54
|
+
// 같은 종류끼리는 연결 이름만 다른 같은 경로라(예: 커넥터 MCP와 직접 띄운 MCP) 대체로 치지 않는다
|
|
55
|
+
const reasons = [];
|
|
56
|
+
for (const access of source.frontmatter.access) {
|
|
57
|
+
const reason = unmetReason(access, avail, env);
|
|
58
|
+
if (!reason) {
|
|
59
|
+
const other = reasons.find((r) => r.kind !== access.kind);
|
|
60
|
+
const server = access.kind === 'mcp' ? findMcpServer(access, avail) : undefined;
|
|
61
|
+
const mcp = server ? { mcpServer: server } : {};
|
|
62
|
+
return !other
|
|
63
|
+
? { status: 'usable', via: access.kind, ...mcp, note: '사용 가능' }
|
|
64
|
+
: {
|
|
65
|
+
status: 'fallback',
|
|
66
|
+
via: access.kind,
|
|
67
|
+
...mcp,
|
|
68
|
+
note: `${other.reason}이라 ${VIA_LABEL[access.kind]}로`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
reasons.push({ kind: access.kind, reason });
|
|
72
|
+
}
|
|
73
|
+
return { status: 'skipped', note: `${reasons[0].reason}이라 건너뜀` };
|
|
74
|
+
}
|
|
75
|
+
export function buildSourceReport(sources, avail, env = process.env, stageFilter) {
|
|
76
|
+
const rows = [];
|
|
77
|
+
const evidenceLimits = [];
|
|
78
|
+
for (const s of sources) {
|
|
79
|
+
const fm = s.frontmatter;
|
|
80
|
+
if (stageFilter && !fm.stage.includes(stageFilter))
|
|
81
|
+
continue;
|
|
82
|
+
const ev = evaluateSource(s, avail, env);
|
|
83
|
+
rows.push({
|
|
84
|
+
name: fm.name,
|
|
85
|
+
description: fm.description,
|
|
86
|
+
stage: fm.stage,
|
|
87
|
+
origin: s.origin,
|
|
88
|
+
overridden: s.overridden,
|
|
89
|
+
evidence: fm.evidence,
|
|
90
|
+
status: ev.status,
|
|
91
|
+
...(ev.via ? { via: ev.via } : {}),
|
|
92
|
+
...(ev.mcpServer ? { mcpServer: ev.mcpServer } : {}),
|
|
93
|
+
note: ev.note,
|
|
94
|
+
...(fm.limits ? { limits: fm.limits } : {}),
|
|
95
|
+
});
|
|
96
|
+
if (ev.status === 'skipped') {
|
|
97
|
+
const tail = fm.fallback ? `${fm.fallback}` : '이 출처가 맡던 부분은 못 채웠다';
|
|
98
|
+
evidenceLimits.push(`${fm.name}: ${ev.note.replace(/이라 건너뜀$/, '')}이라 쓰지 못했다. ${tail}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
if (ev.status === 'fallback')
|
|
102
|
+
evidenceLimits.push(`${fm.name}: ${ev.note}`);
|
|
103
|
+
if (fm.limits)
|
|
104
|
+
evidenceLimits.push(`${fm.name}: ${fm.limits}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { sources: rows, evidenceLimits };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=source-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-status.js","sourceRoot":"","sources":["../../../src/research/source-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Research } from './schema.js';
|
|
2
|
+
export type ResearchAudience = 'private' | 'shared';
|
|
3
|
+
export declare const SLUG_PATTERN: RegExp;
|
|
4
|
+
export declare function isValidSlug(slug: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* 리서치 결과 저장소: `<repoRoot>/.gestalt/research/<slug>/`.
|
|
7
|
+
* `.gestalt/`는 gitignore 대상이라 캡처와 근거 자료가 커밋에 딸려가지 않는다.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ResearchStore {
|
|
10
|
+
private readonly root;
|
|
11
|
+
constructor(repoRoot: string);
|
|
12
|
+
/** slug는 경로 조각이라 패턴을 통과한 것만 받는다 */
|
|
13
|
+
dir(slug: string): string;
|
|
14
|
+
capturesDir(slug: string): string;
|
|
15
|
+
jsonPath(slug: string): string;
|
|
16
|
+
htmlPath(slug: string, audience: ResearchAudience): string;
|
|
17
|
+
/** 없으면 null. 깨졌거나 스키마에 안 맞으면 `.corrupt-*`로 옮기고 null */
|
|
18
|
+
load(slug: string): Research | null;
|
|
19
|
+
save(research: Research): string;
|
|
20
|
+
saveHtml(slug: string, audience: ResearchAudience, html: string): string;
|
|
21
|
+
/** research 폴더 바로 아래에 파생 파일(figma-plan.json 등)을 쓴다 */
|
|
22
|
+
saveFile(slug: string, name: string, content: string): string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/research/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEpD,eAAO,MAAM,YAAY,QAA8B,CAAC;AAExD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,QAAQ,EAAE,MAAM;IAI5B,mCAAmC;IACnC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAKzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI9B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM;IAI1D,uDAAuD;IACvD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAOnC,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAMhC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAOxE,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAM9D"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readJsonOrQuarantine, writeJsonAtomic } from '../core/json-file.js';
|
|
4
|
+
import { parseResearch } from './schema.js';
|
|
5
|
+
export const SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
6
|
+
export function isValidSlug(slug) {
|
|
7
|
+
return SLUG_PATTERN.test(slug);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 리서치 결과 저장소: `<repoRoot>/.gestalt/research/<slug>/`.
|
|
11
|
+
* `.gestalt/`는 gitignore 대상이라 캡처와 근거 자료가 커밋에 딸려가지 않는다.
|
|
12
|
+
*/
|
|
13
|
+
export class ResearchStore {
|
|
14
|
+
root;
|
|
15
|
+
constructor(repoRoot) {
|
|
16
|
+
this.root = join(repoRoot, '.gestalt', 'research');
|
|
17
|
+
}
|
|
18
|
+
/** slug는 경로 조각이라 패턴을 통과한 것만 받는다 */
|
|
19
|
+
dir(slug) {
|
|
20
|
+
if (!isValidSlug(slug))
|
|
21
|
+
throw new Error(`slug가 올바르지 않다: ${slug}`);
|
|
22
|
+
return join(this.root, slug);
|
|
23
|
+
}
|
|
24
|
+
capturesDir(slug) {
|
|
25
|
+
return join(this.dir(slug), 'captures');
|
|
26
|
+
}
|
|
27
|
+
jsonPath(slug) {
|
|
28
|
+
return join(this.dir(slug), 'research.json');
|
|
29
|
+
}
|
|
30
|
+
htmlPath(slug, audience) {
|
|
31
|
+
return join(this.dir(slug), audience === 'shared' ? 'research.shared.html' : 'research.html');
|
|
32
|
+
}
|
|
33
|
+
/** 없으면 null. 깨졌거나 스키마에 안 맞으면 `.corrupt-*`로 옮기고 null */
|
|
34
|
+
load(slug) {
|
|
35
|
+
const raw = readJsonOrQuarantine(this.jsonPath(slug), (v) => parseResearch(v).ok);
|
|
36
|
+
if (raw === undefined)
|
|
37
|
+
return null;
|
|
38
|
+
const parsed = parseResearch(raw);
|
|
39
|
+
return parsed.ok ? parsed.value : null;
|
|
40
|
+
}
|
|
41
|
+
save(research) {
|
|
42
|
+
const path = this.jsonPath(research.meta.slug);
|
|
43
|
+
writeJsonAtomic(path, research);
|
|
44
|
+
return path;
|
|
45
|
+
}
|
|
46
|
+
saveHtml(slug, audience, html) {
|
|
47
|
+
const path = this.htmlPath(slug, audience);
|
|
48
|
+
mkdirSync(this.dir(slug), { recursive: true });
|
|
49
|
+
writeFileSync(path, html, 'utf-8');
|
|
50
|
+
return path;
|
|
51
|
+
}
|
|
52
|
+
/** research 폴더 바로 아래에 파생 파일(figma-plan.json 등)을 쓴다 */
|
|
53
|
+
saveFile(slug, name, content) {
|
|
54
|
+
const path = join(this.dir(slug), name);
|
|
55
|
+
mkdirSync(this.dir(slug), { recursive: true });
|
|
56
|
+
writeFileSync(path, content, 'utf-8');
|
|
57
|
+
return path;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/research/store.ts"],"names":[],"mappings":"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"}
|