careermate 0.0.1 → 0.0.2

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.
Files changed (33) hide show
  1. package/INSTALL.md +12 -8
  2. package/README.md +14 -8
  3. package/dist/career-os/knowledge/interview-behavioral.md +9 -1
  4. package/dist/career-os/knowledge/offer-evaluation-decision.md +2 -2
  5. package/dist/career-os/knowledge/onboarding-first-90-days.md +1 -1
  6. package/dist/career-os/knowledge/verifiers/human-voice.md +20 -0
  7. package/dist/doctor.mjs +421 -11
  8. package/dist/mcp.mjs +421 -11
  9. package/dist/migrate.mjs +3 -1
  10. package/dist/public/index.html +1 -0
  11. package/dist/public/pages/settings.js +45 -0
  12. package/dist/public/styles.css +2 -0
  13. package/dist/site/index.html +19 -12
  14. package/dist/site/llms-install.txt +11 -8
  15. package/dist/site/start.html +16 -9
  16. package/dist/site/style.css +9 -0
  17. package/dist/web.mjs +351 -6
  18. package/docs/SUPPORTED_AI_APPS.md +6 -6
  19. package/docs/career-os/knowledge/interview-behavioral.md +9 -1
  20. package/docs/career-os/knowledge/offer-evaluation-decision.md +2 -2
  21. package/docs/career-os/knowledge/onboarding-first-90-days.md +1 -1
  22. package/docs/career-os/knowledge/verifiers/human-voice.md +20 -0
  23. package/package.json +1 -1
  24. package/packages/core/src/index.ts +1 -0
  25. package/packages/core/src/services.ts +61 -2
  26. package/packages/core/src/verify/index.ts +11 -0
  27. package/packages/core/src/verify/lexicons.ts +149 -0
  28. package/packages/core/src/verify/lint.ts +248 -0
  29. package/packages/core/src/verify/normalize.ts +112 -0
  30. package/packages/db/src/index.ts +1 -0
  31. package/packages/db/src/settings.ts +41 -0
  32. package/packages/mcp-tools/src/tools.ts +80 -6
  33. package/packages/shared/src/schemas.ts +8 -0
package/dist/doctor.mjs CHANGED
@@ -60747,8 +60747,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
60747
60747
  return [g, g, g];
60748
60748
  }
60749
60749
  static G_HTML([g]) {
60750
- const G = makeColorComp(g);
60751
- return `#${G}${G}${G}`;
60750
+ const G2 = makeColorComp(g);
60751
+ return `#${G2}${G2}${G2}`;
60752
60752
  }
60753
60753
  static RGB_G([r, g, b]) {
60754
60754
  return ["G", 0.3 * r + 0.59 * g + 0.11 * b];
@@ -71431,6 +71431,26 @@ function fromJson(text, fallback) {
71431
71431
  var toBit = (b) => b ? 1 : 0;
71432
71432
  var fromBit = (n) => Number(n) === 1;
71433
71433
 
71434
+ // packages/db/src/settings.ts
71435
+ var SETTING_PREFIX = "setting.";
71436
+ function getSetting(key) {
71437
+ const row = getDb().prepare(`SELECT value FROM _meta WHERE key = ?`).get(SETTING_PREFIX + key);
71438
+ return row ? row.value : null;
71439
+ }
71440
+ function setSetting(key, value) {
71441
+ getDb().prepare(
71442
+ `INSERT INTO _meta (key, value) VALUES (?, ?)
71443
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
71444
+ ).run(SETTING_PREFIX + key, value);
71445
+ }
71446
+ function getVerifyStrict() {
71447
+ return getSetting("verify_strict") === "1";
71448
+ }
71449
+ function setVerifyStrict(on) {
71450
+ setSetting("verify_strict", on ? "1" : "0");
71451
+ return on;
71452
+ }
71453
+
71434
71454
  // packages/shared/src/enums.ts
71435
71455
  var APPLICATION_STATUSES = [
71436
71456
  "draft",
@@ -75707,7 +75727,9 @@ var CoverLetterVersionInputSchema = external_exports.object({
75707
75727
  content: reqBody,
75708
75728
  note: optNote.describe("\uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D (\uC608: \uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15)"),
75709
75729
  source: external_exports.enum(CONTENT_SOURCES).optional(),
75710
- set_current: external_exports.boolean().optional().describe("\uC774 \uBC84\uC804\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uC9C0\uC815 (\uAE30\uBCF8 true)")
75730
+ set_current: external_exports.boolean().optional().describe("\uC774 \uBC84\uC804\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uC9C0\uC815 (\uAE30\uBCF8 true)"),
75731
+ force: external_exports.boolean().optional().describe("\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80(\uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58)\uC5D0\uC11C \uB9C9\uD614\uC744 \uB54C, \uC758\uB3C4\uD55C \uAC12\uC784\uC744 \uD655\uC778\uD558\uACE0 \uADF8\uB300\uB85C \uC800\uC7A5"),
75732
+ strict: external_exports.boolean().optional().describe("\uC774\uBC88 \uC800\uC7A5\uB9CC \uC5C4\uACA9\uD558\uAC8C \uC810\uAC80(\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58\uB3C4 \uCC28\uB2E8). \uC0DD\uB7B5\uD558\uBA74 \uC800\uC7A5\uB41C \uBAA8\uB4DC \uC124\uC815\uC744 \uB530\uB984")
75711
75733
  });
75712
75734
  var CoverLetterVersionRecordSchema = external_exports.object({
75713
75735
  id,
@@ -75869,7 +75891,7 @@ var ApplicationContextSchema = external_exports.object({
75869
75891
  });
75870
75892
 
75871
75893
  // packages/shared/src/build-info.ts
75872
- var APP_VERSION = true ? "0.0.1" : "0.0.0-dev";
75894
+ var APP_VERSION = true ? "0.0.2" : "0.0.0-dev";
75873
75895
  var BUNDLED = true;
75874
75896
 
75875
75897
  // packages/shared/src/index.ts
@@ -76685,7 +76707,333 @@ function getApplicationContext(opts = {}) {
76685
76707
  };
76686
76708
  }
76687
76709
 
76710
+ // packages/core/src/verify/normalize.ts
76711
+ var ZERO_WIDTH = /[​-‍⁠]/g;
76712
+ function normalizeText(s) {
76713
+ return (s ?? "").normalize("NFKC").replace(ZERO_WIDTH, "");
76714
+ }
76715
+ var MAGNITUDE = { "\uC5B5": 1e8, "\uB9CC": 1e4, "\uCC9C": 1e3 };
76716
+ var RISK_UNITS = /* @__PURE__ */ new Set([
76717
+ "%",
76718
+ "\uBC30",
76719
+ "\uC6D0",
76720
+ "\uAC74",
76721
+ "\uBA85",
76722
+ "\uC810",
76723
+ "\uC704",
76724
+ "\uB144\uCC28",
76725
+ "\uB144",
76726
+ "\uAC1C\uC6D4",
76727
+ "\uC8FC",
76728
+ "\uC2DC\uAC04",
76729
+ "\uD68C",
76730
+ "\uAC1C"
76731
+ ]);
76732
+ var NUM_RE = /(\d{1,3}(?:,\d{3})+|\d+)(?:\.(\d+))?\s*(억|만|천)?\s*(원|%p|%|%|퍼센트|배|건|명|점|위|년차|개월|년|시간|주|회|개)?/g;
76733
+ var YEAR_RE = /^(?:19|20)\d{2}$/;
76734
+ function normUnit(u) {
76735
+ if (u === "\uFF05" || u === "\uD37C\uC13C\uD2B8" || u === "%p") return "%";
76736
+ return u;
76737
+ }
76738
+ function extractNumbers(text) {
76739
+ const t = normalizeText(text);
76740
+ const out = [];
76741
+ for (const m of t.matchAll(NUM_RE)) {
76742
+ const intPart = m[1].replace(/,/g, "");
76743
+ const frac = m[2] ? "." + m[2] : "";
76744
+ const base = Number(intPart + frac);
76745
+ if (!Number.isFinite(base)) continue;
76746
+ const mag = m[3] ?? "";
76747
+ let unit = normUnit(m[4] ?? "");
76748
+ if (!mag && (!unit || unit === "\uB144") && YEAR_RE.test(intPart) && !frac) continue;
76749
+ const scale = MAGNITUDE[mag] ?? 1;
76750
+ const value = base * scale;
76751
+ if (mag && !unit) unit = "\uC6D0";
76752
+ const surface = m[0].trim();
76753
+ if (!surface) continue;
76754
+ const risk = RISK_UNITS.has(unit) || !unit && value >= 1e3 || !!mag && !unit;
76755
+ out.push({ raw: surface, value, unit, risk });
76756
+ }
76757
+ return out;
76758
+ }
76759
+ function splitSegments(text) {
76760
+ return normalizeText(text).split(/[\n.。!?!?·••]|(?:\s-\s)/).map((s) => s.trim()).filter(Boolean);
76761
+ }
76762
+ function addToMap(map2, unit, value) {
76763
+ const arr = map2.get(unit);
76764
+ if (arr) arr.push(value);
76765
+ else map2.set(unit, [value]);
76766
+ }
76767
+ function buildCorpus(text) {
76768
+ const byUnit = /* @__PURE__ */ new Map();
76769
+ const segments = [];
76770
+ for (const seg of splitSegments(text)) {
76771
+ const segMap = /* @__PURE__ */ new Map();
76772
+ for (const n of extractNumbers(seg)) {
76773
+ addToMap(byUnit, n.unit, n.value);
76774
+ addToMap(segMap, n.unit, n.value);
76775
+ }
76776
+ if (segMap.size) segments.push(segMap);
76777
+ }
76778
+ return { byUnit, segments };
76779
+ }
76780
+
76781
+ // packages/core/src/verify/lexicons.ts
76782
+ var G = (src, flags = "g") => new RegExp(src, flags);
76783
+ var GENERIC_OPENERS = {
76784
+ id: "generic_opener",
76785
+ label: "\uC81C\uB108\uB9AD\xB7\uC0C1\uD22C\uC801 \uB3C4\uC785(\uD68C\uC0AC\uBA85 \uBC14\uAFD4\uB3C4 \uB9D0\uC774 \uB418\uB294 \uC2DC\uC791)",
76786
+ regex: G(
76787
+ [
76788
+ "i am writing to apply",
76789
+ "i have always been passionate",
76790
+ "as a recent graduate",
76791
+ "to whom it may concern",
76792
+ "\uC9C0\uC6D0\uD558\uAC8C \uB418\uC5C8\uC2B5\uB2C8\uB2E4",
76793
+ "\uC9C0\uC6D0\uD569\uB2C8\uB2E4",
76794
+ "\uC5B4\uB9B4 ?\uC801\uBD80\uD130",
76795
+ "\uBCF4\uBA70 \uC790\uB790\uC2B5\uB2C8\uB2E4",
76796
+ "\uC800\uB294 .{0,10}\uD55C \uC0AC\uB78C\uC785\uB2C8\uB2E4",
76797
+ "\uD3C9\uC18C .{0,14}\uAD00\uC2EC\uC774 \uB9CE\uC544",
76798
+ "\uD56D\uC0C1 .{0,12}\uBBFF\uC5B4",
76799
+ "\uB370\uC774\uD130\uC758 \uD798\uC744 \uBBFF"
76800
+ ].join("|"),
76801
+ "gi"
76802
+ )
76803
+ };
76804
+ var EMPTY_ADJECTIVES = {
76805
+ id: "empty_adjective",
76806
+ label: "\uC99D\uAC70 \uC5C6\uB294 \uD3C9\uAC00\uD615 \uD615\uC6A9\uC0AC\xB7\uC790\uAE30\uC11C\uC220",
76807
+ regex: G(
76808
+ [
76809
+ "\uC131\uC2E4(\uD558|\uD55C|\uD568)",
76810
+ "\uCC45\uC784\uAC10\uC774 \uAC15",
76811
+ "\uC5F4\uC815(\uC744 \uAC00\uC9C0|\uC801)",
76812
+ "\uC18C\uD1B5\uC744 \uC798",
76813
+ "\uCEE4\uBBA4\uB2C8\uCF00\uC774\uC158\uC774 \uC6D0\uD65C",
76814
+ "\uAF3C\uAF3C(\uD558|\uD55C|\uD568)",
76815
+ "\uB048\uAE30(\uC788|\uAC00)",
76816
+ "\uD5CC\uC2E0\uC801",
76817
+ "\uC8FC\uB3C4\uC801\uC778",
76818
+ "\uC131\uC7A5\uD558\uB294 \uC778\uC7AC",
76819
+ "\uB77C\uACE0 \uC0DD\uAC01\uD569\uB2C8\uB2E4",
76820
+ "\uB9CE\uC740 \uAC83\uC744 \uBC30\uC6E0",
76821
+ "\uD1B5\uD574 \uC131\uC7A5",
76822
+ "\uAE30\uC5EC\uD558\uACE0 \uC2F6",
76823
+ "\uCD5C\uC120\uC744 \uB2E4",
76824
+ "\uBCF4\uD0EC\uC774 \uB418\uACA0",
76825
+ "team player",
76826
+ "hard-?working",
76827
+ "detail-?oriented",
76828
+ "motivated",
76829
+ "proactive",
76830
+ "dedicated",
76831
+ "diligent"
76832
+ ].join("|"),
76833
+ "gi"
76834
+ )
76835
+ };
76836
+ var CLICHES = {
76837
+ id: "cliche",
76838
+ label: "AI \uD074\uB9AC\uC170\xB7\uACF5\uD5C8\uD55C \uC0C1\uD22C\uC5B4",
76839
+ regex: G(
76840
+ [
76841
+ "\uAE09\uBCC0\uD558\uB294 \uC2DC\uB300",
76842
+ "4\uCC28 \uC0B0\uC5C5\uD601\uBA85",
76843
+ "\uB04A\uC784\uC5C6\uB294 \uB178\uB825",
76844
+ "\uADC0\uC0AC\uC758 \uBC1C\uC804\uC5D0 \uAE30\uC5EC",
76845
+ "\uADC0\uC0AC",
76846
+ "\uC18C\uD1B5\uACFC \uD611\uC5C5",
76847
+ "\uAE00\uB85C\uBC8C \uC778\uC7AC",
76848
+ "\uBB34\uD55C\uD55C \uAC00\uB2A5\uC131",
76849
+ "\uD55C \uB2E8\uACC4 \uB354 \uC131\uC7A5"
76850
+ ].join("|")
76851
+ )
76852
+ };
76853
+ var TRANSLATIONESE = {
76854
+ id: "translationese",
76855
+ label: "\uBC88\uC5ED\uD22C \uBB38\uBC95(\uC601\uC5B4 \uC9C1\uC5ED\uCCB4)",
76856
+ regex: G(
76857
+ [
76858
+ "\uC5D0 \uB300\uD55C",
76859
+ "\uC744 \uD1B5\uD574",
76860
+ "\uB97C \uD1B5\uD574",
76861
+ "\uC5D0 \uC788\uC5B4\uC11C",
76862
+ "\uB77C\uB294 \uC810\uC5D0\uC11C",
76863
+ "\uAC00\uC9C0\uACE0 \uC788\uC2B5\uB2C8\uB2E4",
76864
+ "\uB418\uC5B4\uC84C",
76865
+ "\uB418\uC5B4\uC9D1\uB2C8\uB2E4",
76866
+ "\uD568\uC73C\uB85C\uC368",
76867
+ "\uC5D0 \uB2E4\uB984 \uC544\uB2C8",
76868
+ "\uC9C4\uD589\uD558\uC600\uC2B5\uB2C8\uB2E4"
76869
+ ].join("|")
76870
+ )
76871
+ };
76872
+ var HYPE = {
76873
+ id: "hype",
76874
+ label: "\uACFC\uC7A5 \uC218\uC2DD\uC5B4",
76875
+ regex: G(["\uB9E4\uC6B0", "\uC815\uB9D0\uB85C", "\uD0C1\uC6D4(\uD55C|\uD558\uAC8C)", "\uC131\uACF5\uC801\uC73C\uB85C", "\uC644\uBCBD(\uD55C|\uD558\uAC8C)", "\uC5C4\uCCAD\uB09C", "\uD68D\uAE30\uC801"].join("|"))
76876
+ };
76877
+ var BLIND_PII = {
76878
+ id: "blind_pii",
76879
+ label: "\uBE14\uB77C\uC778\uB4DC \uCC44\uC6A9 \uC704\uBC18 \uC18C\uC9C0(\uC9C1\uBB34\uBB34\uAD00 \uAC1C\uC778\uC815\uBCF4)",
76880
+ regex: G(["\\d{2,3}\\s?\uC138", "\uC0AC\uC9C4\\s?\uCCA8\uBD80", "\uAC00\uC871\uAD00\uACC4", "\uBCF8\uC801", "\uCD9C\uC2E0\uC9C0", "\uC0DD\uB144\uC6D4\uC77C"].join("|"))
76881
+ };
76882
+ var STYLE_LEXICONS = [
76883
+ GENERIC_OPENERS,
76884
+ EMPTY_ADJECTIVES,
76885
+ CLICHES,
76886
+ TRANSLATIONESE,
76887
+ HYPE,
76888
+ BLIND_PII
76889
+ ];
76890
+ function countHits(lex, text, maxHits = 8) {
76891
+ const hits = [];
76892
+ let count = 0;
76893
+ for (const m of text.matchAll(lex.regex)) {
76894
+ count++;
76895
+ if (hits.length < maxHits) hits.push(m[0].trim());
76896
+ }
76897
+ return { count, hits };
76898
+ }
76899
+
76900
+ // packages/core/src/verify/lint.ts
76901
+ var within = (a, b) => Math.abs(a - b) <= Math.max(0.5, 0.02 * Math.max(Math.abs(a), Math.abs(b)));
76902
+ var approx = (a, b) => Math.abs(a - b) <= Math.max(0.5, 0.01 * Math.max(Math.abs(a), Math.abs(b)));
76903
+ function isExplained(token, corpus) {
76904
+ const v = token.value;
76905
+ for (const s of corpus.byUnit.get(token.unit) ?? []) if (within(v, s)) return true;
76906
+ for (const seg of corpus.segments) {
76907
+ const vals = seg.get(token.unit);
76908
+ if (vals && vals.length >= 2) {
76909
+ for (let i2 = 0; i2 < vals.length; i2++)
76910
+ for (let j = 0; j < vals.length; j++) {
76911
+ if (i2 === j) continue;
76912
+ if (approx(v, vals[i2] - vals[j]) || approx(v, vals[i2] + vals[j])) return true;
76913
+ }
76914
+ }
76915
+ }
76916
+ if (token.unit === "%") {
76917
+ for (const seg of corpus.segments) {
76918
+ for (const [u, vals] of seg) {
76919
+ if (u === "%" || vals.length < 2) continue;
76920
+ for (let i2 = 0; i2 < vals.length; i2++)
76921
+ for (let j = 0; j < vals.length; j++) {
76922
+ if (i2 === j) continue;
76923
+ const a = vals[i2];
76924
+ const b = vals[j];
76925
+ if (b !== 0 && (approx(v, a / b * 100) || approx(v, (a - b) / b * 100) || approx(v, (a - b) / a * 100)))
76926
+ return true;
76927
+ if (a + b !== 0 && approx(v, a / (a + b) * 100)) return true;
76928
+ }
76929
+ }
76930
+ }
76931
+ }
76932
+ return false;
76933
+ }
76934
+ function analyzeProvenance(outputText, corpus) {
76935
+ const documents = buildCorpus(corpus.documents);
76936
+ const structured = buildCorpus(corpus.structured);
76937
+ const job = buildCorpus(corpus.job);
76938
+ const nums = extractNumbers(outputText).filter((n) => n.risk);
76939
+ const res = { checked: nums.length, supported: 0, fabricated: [], unverified: [], jobSourced: [] };
76940
+ for (const n of nums) {
76941
+ const tok = { raw: n.raw, value: n.value, unit: n.unit };
76942
+ if (isExplained(n, documents)) res.supported++;
76943
+ else if (isExplained(n, structured)) res.unverified.push({ ...tok, reason: "structured_only" });
76944
+ else if (isExplained(n, job)) res.jobSourced.push({ ...tok, reason: "job_only" });
76945
+ else res.fabricated.push({ ...tok, reason: "fabricated" });
76946
+ }
76947
+ return res;
76948
+ }
76949
+ function styleSignals(text) {
76950
+ const t = normalizeText(text);
76951
+ const signals = [];
76952
+ for (const lex of STYLE_LEXICONS) {
76953
+ const { count, hits } = countHits(lex, t);
76954
+ if (count > 0) {
76955
+ const severity = lex.id === "blind_pii" || lex.id === "generic_opener" ? "warn" : "info";
76956
+ signals.push({ id: lex.id, label: lex.label, severity, count, hits });
76957
+ }
76958
+ }
76959
+ return signals;
76960
+ }
76961
+ var DISCLAIMER = "\uC774 \uC810\uAC80\uC740 LLM \uC5C6\uC774 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4(\uC22B\uC790 \uCD9C\uCC98\xB7\uBB38\uCCB4 \uC2E0\uD638). \uD1B5\uACFC=\uD569\uACA9\uC774 \uC544\uB2C8\uBA70, \uC0AC\uC2E4\xB7\uB3D9\uBB38\uC11C\uB2F5\xB7\uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD574\uC57C \uD569\uB2C8\uB2E4.";
76962
+ function lintArtifact(kind, text, corpus, opts = {}) {
76963
+ const norm = normalizeText(text);
76964
+ const corpusAvailable = (corpus.documents + corpus.structured).trim().length > 0;
76965
+ const provenance = analyzeProvenance(text, corpus);
76966
+ const signals = styleSignals(text);
76967
+ const strict = !!opts.strict;
76968
+ const blocking = [];
76969
+ if (corpusAvailable && kind === "cover_letter") {
76970
+ if (provenance.fabricated.length > 0) {
76971
+ blocking.push({
76972
+ id: "fabricated_numbers",
76973
+ label: "\uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58(\uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\uC5D0 \uC5C6\uC74C)",
76974
+ detail: provenance.fabricated.map((f) => f.raw).join(", ")
76975
+ });
76976
+ }
76977
+ if (strict && provenance.unverified.length > 0) {
76978
+ blocking.push({
76979
+ id: "unverified_numbers_strict",
76980
+ label: "\uC5C4\uACA9 \uBAA8\uB4DC: \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58(\uAD6C\uC870\uD654 \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uC74C)",
76981
+ detail: provenance.unverified.map((u) => u.raw).join(", ")
76982
+ });
76983
+ }
76984
+ }
76985
+ return {
76986
+ kind,
76987
+ charCount: [...norm].length,
76988
+ signals,
76989
+ provenance,
76990
+ blocking,
76991
+ corpusAvailable,
76992
+ strict,
76993
+ disclaimer: DISCLAIMER
76994
+ };
76995
+ }
76996
+ function summarizeReport(r) {
76997
+ const parts = [];
76998
+ if (r.blocking.length)
76999
+ parts.push(`\u26D4 \uC800\uC7A5 \uCC28\uB2E8: ${r.blocking.map((b) => `${b.label}(${b.detail})`).join("; ")}`);
77000
+ if (r.provenance.unverified.length)
77001
+ parts.push(
77002
+ `\uBBF8\uD655\uC778 \uC218\uCE58 ${r.provenance.unverified.length}\uAC1C(${r.provenance.unverified.map((u) => u.raw).join(", ")}) \u2014 \uC774\uB825\uC11C \uBCF8\uBB38\uC5D4 \uC5C6\uACE0 \uAD6C\uC870\uD654 \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uC74C, \uC0AC\uC2E4\uC778\uC9C0 \uD655\uC778`
77003
+ );
77004
+ if (r.provenance.jobSourced.length)
77005
+ parts.push(`\uACF5\uACE0 \uCD9C\uCC98 \uC218\uCE58 ${r.provenance.jobSourced.length}\uAC1C(\uBCF8\uC778 \uC131\uACFC\uB85C \uC4F0\uC9C0 \uC54A\uC558\uB294\uC9C0 \uD655\uC778)`);
77006
+ const adv = r.signals.filter((s) => s.count > 0);
77007
+ if (adv.length) parts.push("\uBB38\uCCB4 \uC2E0\uD638: " + adv.map((s) => `${s.label.split("(")[0]}\xD7${s.count}`).join(", "));
77008
+ if (!parts.length) parts.push("\uC790\uB3D9 \uC810\uAC80 \uD1B5\uACFC(\uC140 \uC218 \uC788\uB294 \uD56D\uBAA9 \uAE30\uC900). \uC758\uBBF8 \uD310\uB2E8\uC740 \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.");
77009
+ return parts.join(" \xB7 ");
77010
+ }
77011
+
76688
77012
  // packages/core/src/services.ts
77013
+ function collectVerifyCorpus(jobId) {
77014
+ const documents = [];
77015
+ for (const d of documentRepo.list()) {
77016
+ if (d.source !== "ai") documents.push(d.content);
77017
+ }
77018
+ const structured = [];
77019
+ for (const e of experienceRepo.list()) {
77020
+ structured.push([e.company, e.role, e.description, ...e.achievements ?? [], ...e.tech ?? []].filter(Boolean).join("\n"));
77021
+ }
77022
+ for (const p of projectRepo.list()) {
77023
+ structured.push([p.name, p.description, ...p.highlights ?? [], ...p.tech ?? []].filter(Boolean).join("\n"));
77024
+ }
77025
+ for (const s of skillRepo.list()) {
77026
+ structured.push([s.name, s.level, s.years != null ? `${s.years}\uB144` : ""].filter(Boolean).join(" "));
77027
+ }
77028
+ const job = jobId ? jobRepo.get(jobId) : null;
77029
+ const jobText = job ? [job.description, ...job.requirements ?? [], ...job.keywords ?? []].filter(Boolean).join("\n") : "";
77030
+ return { documents: documents.join("\n"), structured: structured.join("\n"), job: jobText };
77031
+ }
77032
+ function previewCoverLetter(text, jobId, strict) {
77033
+ return lintArtifact("cover_letter", text, collectVerifyCorpus(jobId), {
77034
+ strict: strict ?? getVerifyStrict()
77035
+ });
77036
+ }
76689
77037
  function saveProfile(input) {
76690
77038
  const p = profileRepo.save(input);
76691
77039
  activityRepo.log("profile_updated", `\uD504\uB85C\uD544\uC744 ${p.name ? `(${p.name}) ` : ""}\uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4.`, "profile", p.id);
@@ -76727,6 +77075,15 @@ function saveFitAnalysis(input) {
76727
77075
  return fit;
76728
77076
  }
76729
77077
  function saveCoverLetterVersion(input) {
77078
+ const strict = input.strict ?? getVerifyStrict();
77079
+ const verification = lintArtifact("cover_letter", input.content, collectVerifyCorpus(input.job_id), { strict });
77080
+ const forced = verification.blocking.length > 0 && input.force === true;
77081
+ if (verification.blocking.length > 0 && !forced) {
77082
+ const detail = verification.blocking.map((b) => `${b.label}: ${b.detail}`).join("; ");
77083
+ const strictHit = verification.blocking.some((b) => b.id === "unverified_numbers_strict");
77084
+ const tail = strictHit ? `\uC5C4\uACA9 \uBAA8\uB4DC\uAC00 \uCF1C\uC838 \uC788\uC5B4, \uC62C\uB9B0 \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uC5C6\uB294 \uC218\uCE58\uB294 \uB9C9\uD799\uB2C8\uB2E4. \uC774\uB825\uC11C\uB97C \uC62C\uB9AC\uAC70\uB098 \uC5C4\uACA9 \uBAA8\uB4DC\uB97C \uB044\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force: true\uB85C \uC800\uC7A5\uD558\uC138\uC694.` : `\uC774 \uC218\uCE58\uB294 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4. \uC6D0\uBCF8 \uB370\uC774\uD130\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774 \uB9DE\uB2E4\uBA74 \uBA3C\uC800 \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADF8 \uC218\uCE58\uB97C \uCD94\uAC00\uD55C \uB4A4 \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694(\uADF8\uB300\uB85C \uC800\uC7A5\uD558\uB824\uBA74 force: true).`;
77085
+ throw new Error(`\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC5D0\uC11C \uB9C9\uD614\uC2B5\uB2C8\uB2E4 \u2014 ${detail}. ${tail}`);
77086
+ }
76730
77087
  const { coverLetter, version: version2 } = coverLetterRepo.addVersion({
76731
77088
  cover_letter_id: input.cover_letter_id,
76732
77089
  title: input.title,
@@ -76748,7 +77105,7 @@ function saveCoverLetterVersion(input) {
76748
77105
  "cover_letter",
76749
77106
  coverLetter.id
76750
77107
  );
76751
- return { coverLetter, version: version2 };
77108
+ return { coverLetter, version: version2, verification, forced };
76752
77109
  }
76753
77110
  function updateApplicationStatus(jobId, status, note) {
76754
77111
  const job = jobRepo.get(jobId);
@@ -78589,16 +78946,22 @@ ${summary}
78589
78946
  const s = getOnboardingStatus();
78590
78947
  const lines = s.next_steps.map((t, i2) => `${i2 + 1}. ${t}`).join("\n");
78591
78948
  const upd = getUpdateStatus();
78949
+ const strict = getVerifyStrict();
78592
78950
  let text = `\uC628\uBCF4\uB529 ${s.completed ? "\uC644\uB8CC\uB428" : "\uC9C4\uD589 \uD544\uC694"} \xB7 \uD504\uB85C\uD544 \uC644\uC131\uB3C4 ${s.profile_completeness}%
78593
78951
 
78594
78952
  \uB2E4\uC74C \uB2E8\uACC4:
78595
78953
  ${lines}`;
78954
+ if (!s.completed) {
78955
+ text += `
78956
+
78957
+ \u{1F4A1} \uCC38\uACE0: \uC790\uC18C\uC11C\uB97C \uC800\uC7A5\uD560 \uB54C CareerMate\uAC00 \uBCF8\uBB38\uC758 \uC218\uCE58\uB97C \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\uC640 \uC790\uB3D9 \uB300\uC870\uD574, \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58\uB294 \uB9C9\uC2B5\uB2C8\uB2E4. \uB354 \uAE50\uAE50\uD558\uAC8C \uBCF4\uAE38 \uC6D0\uD558\uBA74 "\uC5C4\uACA9\uD558\uAC8C \uBD10\uC918"\uB77C\uACE0 \uD558\uAC70\uB098 \uB300\uC2DC\uBCF4\uB4DC \uC124\uC815\uC5D0\uC11C \uC5C4\uACA9 \uBAA8\uB4DC\uB97C \uCF24 \uC218 \uC788\uC5B4\uC694(\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uC5C6\uB294 \uC218\uCE58\uAE4C\uC9C0 \uCC28\uB2E8).`;
78958
+ }
78596
78959
  if (upd.update_available) {
78597
78960
  text += `
78598
78961
 
78599
78962
  \u{1F195} \uC0C8 \uBC84\uC804 v${upd.latest} \uC0AC\uC6A9 \uAC00\uB2A5 (\uD604\uC7AC v${upd.current}). \uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 update_careermate \uB3C4\uAD6C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.`;
78600
78963
  }
78601
- return ok(text, { ...s, update: upd });
78964
+ return ok(text, { ...s, verify_strict: strict, update: upd });
78602
78965
  }
78603
78966
  },
78604
78967
  /* --------------------------------------------------------------- profile */
@@ -78732,11 +79095,20 @@ ${lines}`;
78732
79095
  {
78733
79096
  name: "save_cover_letter_version",
78734
79097
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uBC84\uC804 \uC800\uC7A5",
78735
- description: '\uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uC0C8 \uBC84\uC804\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. CareerMate \uC790\uC18C\uC11C \uC791\uC131 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uD575\uC2EC \uC800\uC7A5 \uB2E8\uACC4\uC785\uB2C8\uB2E4. cover_letter_id\uB97C \uC8FC\uBA74 \uAE30\uC874 \uC790\uC18C\uC11C\uC5D0 \uC0C8 \uBC84\uC804\uC744 \uCD94\uAC00\uD558\uACE0, \uC5C6\uC73C\uBA74 \uC0C8 \uC790\uC18C\uC11C\uB97C \uB9CC\uB4E4\uC5B4 v1\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC774\uB54C title \uAD8C\uC7A5). note\uC5D0 \uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D(\uC608: "\uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15")\uC744 \uB0A8\uAE30\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uBC84\uC804 \uD788\uC2A4\uD1A0\uB9AC\uB97C \uC774\uD574\uD558\uAE30 \uC27D\uC2B5\uB2C8\uB2E4. job_id\uB85C \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD558\uBA74 \uC9C0\uC6D0 \uD56D\uBAA9\uACFC \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4.',
79098
+ description: '\uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uC0C8 \uBC84\uC804\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. CareerMate \uC790\uC18C\uC11C \uC791\uC131 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uD575\uC2EC \uC800\uC7A5 \uB2E8\uACC4\uC785\uB2C8\uB2E4. cover_letter_id\uB97C \uC8FC\uBA74 \uAE30\uC874 \uC790\uC18C\uC11C\uC5D0 \uC0C8 \uBC84\uC804\uC744 \uCD94\uAC00\uD558\uACE0, \uC5C6\uC73C\uBA74 \uC0C8 \uC790\uC18C\uC11C\uB97C \uB9CC\uB4E4\uC5B4 v1\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC774\uB54C title \uAD8C\uC7A5). note\uC5D0 \uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D(\uC608: "\uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15")\uC744 \uB0A8\uAE30\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uBC84\uC804 \uD788\uC2A4\uD1A0\uB9AC\uB97C \uC774\uD574\uD558\uAE30 \uC27D\uC2B5\uB2C8\uB2E4. job_id\uB85C \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD558\uBA74 \uC9C0\uC6D0 \uD56D\uBAA9\uACFC \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4. \uC800\uC7A5 \uC9C1\uC804 \uC790\uB3D9\uC73C\uB85C "\uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58" \uC810\uAC80\uC744 \uB3CC\uB9BD\uB2C8\uB2E4 \u2014 \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC5C6\uC73C\uBA74(\uD658\uAC01 \uC758\uC2EC) \uC800\uC7A5\uC774 \uB9C9\uD799\uB2C8\uB2E4. \uADF8\uB54C\uB294 \uC6D0\uBCF8\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force:true\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694.',
78736
79099
  inputSchema: CoverLetterVersionInputSchema.shape,
78737
79100
  handler: (args) => {
78738
- const { coverLetter, version: version2 } = saveCoverLetterVersion(args);
78739
- return ok(`'${coverLetter.title}' v${version2.version_no}\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4.`, { coverLetter, version: version2 });
79101
+ try {
79102
+ const { coverLetter, version: version2, verification, forced } = saveCoverLetterVersion(args);
79103
+ const advisory = summarizeReport(verification);
79104
+ const head = `'${coverLetter.title}' v${version2.version_no}\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4.`;
79105
+ const hasAdvisory = verification.signals.length || verification.provenance.jobSourced.length || verification.provenance.unverified.length;
79106
+ const note = forced ? "\n\u26A0\uFE0F \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58\uAC00 \uC788\uB294\uB370\uB3C4 force\uB85C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uAC80\uC99D \uBBF8\uD1B5\uACFC\uB85C \uAE30\uB85D). \uBA74\uC811 \uC804 \uC0AC\uC2E4 \uD655\uC778\uC744 \uAD8C\uD569\uB2C8\uB2E4." : hasAdvisory ? `
79107
+ \uC810\uAC80: ${advisory}` : "";
79108
+ return ok(head + note, { coverLetter, version: version2, verification });
79109
+ } catch (e) {
79110
+ return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
79111
+ }
78740
79112
  }
78741
79113
  },
78742
79114
  /* ------------------------------------------------------------------ jobs */
@@ -78966,7 +79338,7 @@ ${lines}`;
78966
79338
  handler: (args) => {
78967
79339
  try {
78968
79340
  const content = getPlaybook(args.domain);
78969
- return ok(content, { domain: args.domain, content });
79341
+ return ok(content, { domain: args.domain });
78970
79342
  } catch (e) {
78971
79343
  return fail(e instanceof Error ? e.message : "\uD50C\uB808\uC774\uBD81\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
78972
79344
  }
@@ -78983,12 +79355,50 @@ ${lines}`;
78983
79355
  handler: (args) => {
78984
79356
  try {
78985
79357
  const content = getVerifier(args.id);
78986
- return ok(content, { id: args.id, content });
79358
+ return ok(content, { id: args.id });
78987
79359
  } catch (e) {
78988
79360
  return fail(e instanceof Error ? e.message : "\uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
78989
79361
  }
78990
79362
  }
78991
79363
  },
79364
+ /* ----------------------------------------- career-os: deterministic lint */
79365
+ {
79366
+ name: "validate_cover_letter",
79367
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC790\uB3D9 \uC810\uAC80 (\uC800\uC7A5 \uC804 \uBBF8\uB9AC\uBCF4\uAE30)",
79368
+ description: "\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C save_cover_letter_version\uC73C\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uBA74, \uC800\uC7A5\uD558\uC9C0 \uC54A\uACE0 \uBBF8\uB9AC \uC810\uAC80\uB9CC \uD569\uB2C8\uB2E4. CareerMate\uAC00 LLM \uC5C6\uC774 \uACB0\uC815\uB860\uC73C\uB85C (1) \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC788\uB294\uC9C0(\uC5C6\uC73C\uBA74 \uD658\uAC01 \uC758\uC2EC \u2192 \uC800\uC7A5 \uC2DC \uCC28\uB2E8\uB428), (2) \uACF5\uACE0\uC5D0\uC11C \uAC00\uC838\uC628 \uC218\uCE58\uB97C \uBCF8\uC778 \uC131\uACFC\uCC98\uB7FC \uC4F0\uC9C0 \uC54A\uC558\uB294\uC9C0, (3) \uBC88\uC5ED\uD22C\xB7\uC81C\uB108\uB9AD \uB3C4\uC785\xB7\uBE48 \uD615\uC6A9\uC0AC\xB7\uBE14\uB77C\uC778\uB4DC \uC704\uBC18 \uAC19\uC740 \uBB38\uCCB4 \uC2E0\uD638\uB97C \uC138\uC5B4 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC810\uAC80\uC740 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4 \u2014 \uC0AC\uC2E4\uC131\xB7\uB3D9\uBB38\uC11C\uB2F5 \uAC19\uC740 \uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD558\uC138\uC694. job_id\uB97C \uC8FC\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50\uD569\uB2C8\uB2E4.",
79369
+ inputSchema: {
79370
+ text: external_exports.string().max(2e5).describe("\uC810\uAC80\uD560 \uC790\uAE30\uC18C\uAC1C\uC11C \uBCF8\uBB38"),
79371
+ job_id: external_exports.string().optional().describe("\uC5F0\uACB0\uD560 \uACF5\uACE0(\uC788\uC73C\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50)"),
79372
+ strict: external_exports.boolean().optional().describe('\uC774\uBC88 \uC810\uAC80\uB9CC \uC5C4\uACA9\uD558\uAC8C(\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58\uB3C4 \uCC28\uB2E8). \uC0AC\uC6A9\uC790\uAC00 "\uC5C4\uACA9\uD558\uAC8C \uBD10\uC918"\uB77C\uACE0 \uD560 \uB54C true. \uC0DD\uB7B5\uD558\uBA74 \uC800\uC7A5\uB41C \uBAA8\uB4DC\uB97C \uB530\uB984')
79373
+ },
79374
+ readOnly: true,
79375
+ handler: (args) => {
79376
+ const text = typeof args?.text === "string" ? args.text : "";
79377
+ if (!text.trim()) return fail("\uC810\uAC80\uD560 \uBCF8\uBB38\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.");
79378
+ const report = previewCoverLetter(text, args?.job_id ?? null, args?.strict);
79379
+ const verdict = report.blocking.length ? `\u26D4 \uC774\uB300\uB85C \uC800\uC7A5\uD558\uBA74 \uB9C9\uD799\uB2C8\uB2E4 \u2014 ${report.blocking.map((b) => `${b.label}: ${b.detail}`).join("; ")}` : "\u2705 \uC790\uB3D9 \uCC28\uB2E8 \uD56D\uBAA9 \uC5C6\uC74C(\uC140 \uC218 \uC788\uB294 \uD56D\uBAA9 \uAE30\uC900).";
79380
+ const modeLine = report.strict ? "\n(\uC5C4\uACA9 \uBAA8\uB4DC \uC801\uC6A9 \uC911)" : "";
79381
+ return ok(`${verdict}${modeLine}
79382
+ ${summarizeReport(report)}
79383
+
79384
+ ${report.disclaimer}`, report);
79385
+ }
79386
+ },
79387
+ {
79388
+ name: "set_verify_mode",
79389
+ title: "\uC810\uAC80 \uBAA8\uB4DC \uC124\uC815 (\uAE30\uBCF8/\uC5C4\uACA9)",
79390
+ description: '\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC758 \uC5C4\uACA9\uB3C4\uB97C \uBC14\uAFC9\uB2C8\uB2E4. \uC5C4\uACA9 \uBAA8\uB4DC(strict=true)\uB97C \uCF1C\uBA74, \uC774\uD6C4 \uBAA8\uB4E0 \uC790\uC18C\uC11C \uC800\uC7A5\uC5D0\uC11C "\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58"(\uAD6C\uC870\uD654 \uACBD\uB825 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uB294 \uAC12)\uAE4C\uC9C0 \uCC28\uB2E8\uD569\uB2C8\uB2E4. \uAE30\uBCF8 \uBAA8\uB4DC\uB294 \uCD9C\uCC98\uAC00 \uC544\uC608 \uC5C6\uB294 \uC218\uCE58\uB9CC \uCC28\uB2E8\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uC5C4\uACA9\uD558\uAC8C \uBD10\uC918"\uB77C\uACE0 \uD558\uBA74 \uBC14\uB85C \uCF1C\uC9C0 \uB9D0\uACE0 "\uC55E\uC73C\uB85C \uD56D\uC0C1 \uC5C4\uACA9\uD558\uAC8C \uBCFC\uAE4C\uC694?"\uB77C\uACE0 \uBA3C\uC800 \uD655\uC778\uD55C \uB4A4 \uB3D9\uC758\uD558\uBA74 strict=true\uB85C \uD638\uCD9C\uD558\uC138\uC694. \uB044\uAE30(strict=false)\uB294 \uC548\uC804\uC7A5\uCE58\uB97C \uC57D\uD558\uAC8C \uB9CC\uB4DC\uB294 \uAC83\uC774\uBBC0\uB85C, \uC0AC\uC6A9\uC790\uAC00 \uBA85\uD655\uD788 \uB044\uACA0\uB2E4\uACE0 \uD560 \uB54C\uB9CC \uD638\uCD9C\uD558\uC138\uC694. \uC774\uBC88 \uD55C \uAC74\uB9CC \uC5C4\uACA9\uD558\uAC8C \uBCF4\uB824\uBA74 \uBAA8\uB4DC\uB97C \uBC14\uAFB8\uC9C0 \uB9D0\uACE0 validate_cover_letter / save_cover_letter_version\uC5D0 strict:true\uB97C \uD55C \uBC88\uB9CC \uB118\uAE30\uC138\uC694.',
79391
+ inputSchema: {
79392
+ strict: external_exports.boolean().describe("true=\uC5C4\uACA9 \uCF1C\uAE30, false=\uB044\uAE30(\uBA85\uD655\uD55C \uC0AC\uC6A9\uC790 \uC758\uC0AC\uC77C \uB54C\uB9CC)")
79393
+ },
79394
+ handler: (args) => {
79395
+ const on = setVerifyStrict(!!args?.strict);
79396
+ return ok(
79397
+ on ? '\uC5C4\uACA9 \uC810\uAC80 \uBAA8\uB4DC\uB97C \uCF30\uC2B5\uB2C8\uB2E4. \uC774\uC81C \uC790\uC18C\uC11C\uB97C \uC800\uC7A5\uD560 \uB54C, \uC62C\uB9B0 \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70\uAC00 \uC5C6\uB294 \uC218\uCE58\uB294 \uB9C9\uD799\uB2C8\uB2E4(\uAD6C\uC870\uD654 \uACBD\uB825 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uB294 \uAC12 \uD3EC\uD568). \uB044\uB824\uBA74 "\uC5C4\uACA9 \uBAA8\uB4DC \uAEBC\uC918"\uB77C\uACE0 \uD558\uC138\uC694.' : "\uC5C4\uACA9 \uC810\uAC80 \uBAA8\uB4DC\uB97C \uAED0\uC2B5\uB2C8\uB2E4. \uC774\uC81C \uCD9C\uCC98\uAC00 \uC544\uC608 \uC5C6\uB294 \uC218\uCE58\uB9CC \uCC28\uB2E8\uD569\uB2C8\uB2E4(\uAD6C\uC870\uD654 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uB294 \uAC12\uC740 \uACBD\uACE0\uB9CC).",
79398
+ { strict: on }
79399
+ );
79400
+ }
79401
+ },
78992
79402
  /* --------------------------------------------------- writing style guide */
78993
79403
  {
78994
79404
  name: "get_writing_style_guide",