@rwendell/dossier 0.0.0-stage → 0.1.1

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 (166) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +323 -2
  3. package/agents/dossier-answerer.md +168 -0
  4. package/agents/dossier-proposer.md +215 -0
  5. package/agents/dossier-tailor.md +232 -0
  6. package/dist/answer/prompt.d.ts +17 -0
  7. package/dist/answer/prompt.js +44 -0
  8. package/dist/answer/prompt.js.map +1 -0
  9. package/dist/answer/run.d.ts +21 -0
  10. package/dist/answer/run.js +272 -0
  11. package/dist/answer/run.js.map +1 -0
  12. package/dist/career/import-resume.d.ts +17 -0
  13. package/dist/career/import-resume.js +194 -0
  14. package/dist/career/import-resume.js.map +1 -0
  15. package/dist/career/load.d.ts +10 -0
  16. package/dist/career/load.js +96 -0
  17. package/dist/career/load.js.map +1 -0
  18. package/dist/career/schema.d.ts +147 -0
  19. package/dist/career/schema.js +95 -0
  20. package/dist/career/schema.js.map +1 -0
  21. package/dist/clean/run.d.ts +1 -0
  22. package/dist/clean/run.js +8 -0
  23. package/dist/clean/run.js.map +1 -0
  24. package/dist/cli/commands/answer.d.ts +23 -0
  25. package/dist/cli/commands/answer.js +110 -0
  26. package/dist/cli/commands/answer.js.map +1 -0
  27. package/dist/cli/commands/clean.d.ts +2 -0
  28. package/dist/cli/commands/clean.js +11 -0
  29. package/dist/cli/commands/clean.js.map +1 -0
  30. package/dist/cli/commands/doctor.d.ts +2 -0
  31. package/dist/cli/commands/doctor.js +77 -0
  32. package/dist/cli/commands/doctor.js.map +1 -0
  33. package/dist/cli/commands/init.d.ts +7 -0
  34. package/dist/cli/commands/init.js +80 -0
  35. package/dist/cli/commands/init.js.map +1 -0
  36. package/dist/cli/commands/paths.d.ts +7 -0
  37. package/dist/cli/commands/paths.js +42 -0
  38. package/dist/cli/commands/paths.js.map +1 -0
  39. package/dist/cli/commands/proposal.d.ts +30 -0
  40. package/dist/cli/commands/proposal.js +148 -0
  41. package/dist/cli/commands/proposal.js.map +1 -0
  42. package/dist/cli/commands/pull-private.d.ts +8 -0
  43. package/dist/cli/commands/pull-private.js +30 -0
  44. package/dist/cli/commands/pull-private.js.map +1 -0
  45. package/dist/cli/commands/score.d.ts +11 -0
  46. package/dist/cli/commands/score.js +58 -0
  47. package/dist/cli/commands/score.js.map +1 -0
  48. package/dist/cli/commands/sync.d.ts +13 -0
  49. package/dist/cli/commands/sync.js +65 -0
  50. package/dist/cli/commands/sync.js.map +1 -0
  51. package/dist/cli/commands/tailor.d.ts +24 -0
  52. package/dist/cli/commands/tailor.js +113 -0
  53. package/dist/cli/commands/tailor.js.map +1 -0
  54. package/dist/cli/commands/verify.d.ts +6 -0
  55. package/dist/cli/commands/verify.js +15 -0
  56. package/dist/cli/commands/verify.js.map +1 -0
  57. package/dist/cli/index.d.ts +2 -0
  58. package/dist/cli/index.js +87 -0
  59. package/dist/cli/index.js.map +1 -0
  60. package/dist/config/config.d.ts +36 -0
  61. package/dist/config/config.js +71 -0
  62. package/dist/config/config.js.map +1 -0
  63. package/dist/config/paths.d.ts +32 -0
  64. package/dist/config/paths.js +121 -0
  65. package/dist/config/paths.js.map +1 -0
  66. package/dist/evidence/numbers.d.ts +2 -0
  67. package/dist/evidence/numbers.js +26 -0
  68. package/dist/evidence/numbers.js.map +1 -0
  69. package/dist/evidence/verify.d.ts +109 -0
  70. package/dist/evidence/verify.js +445 -0
  71. package/dist/evidence/verify.js.map +1 -0
  72. package/dist/job/input.d.ts +9 -0
  73. package/dist/job/input.js +141 -0
  74. package/dist/job/input.js.map +1 -0
  75. package/dist/job/page.d.ts +14 -0
  76. package/dist/job/page.js +426 -0
  77. package/dist/job/page.js.map +1 -0
  78. package/dist/job/types.d.ts +30 -0
  79. package/dist/job/types.js +2 -0
  80. package/dist/job/types.js.map +1 -0
  81. package/dist/llm/agents.d.ts +7 -0
  82. package/dist/llm/agents.js +44 -0
  83. package/dist/llm/agents.js.map +1 -0
  84. package/dist/llm/opencode.d.ts +19 -0
  85. package/dist/llm/opencode.js +117 -0
  86. package/dist/llm/opencode.js.map +1 -0
  87. package/dist/llm/providers.d.ts +63 -0
  88. package/dist/llm/providers.js +207 -0
  89. package/dist/llm/providers.js.map +1 -0
  90. package/dist/private/pull.d.ts +7 -0
  91. package/dist/private/pull.js +168 -0
  92. package/dist/private/pull.js.map +1 -0
  93. package/dist/projects/rank.d.ts +17 -0
  94. package/dist/projects/rank.js +100 -0
  95. package/dist/projects/rank.js.map +1 -0
  96. package/dist/projects/store.d.ts +7 -0
  97. package/dist/projects/store.js +31 -0
  98. package/dist/projects/store.js.map +1 -0
  99. package/dist/projects/sync-github.d.ts +11 -0
  100. package/dist/projects/sync-github.js +269 -0
  101. package/dist/projects/sync-github.js.map +1 -0
  102. package/dist/projects/sync-local.d.ts +8 -0
  103. package/dist/projects/sync-local.js +318 -0
  104. package/dist/projects/sync-local.js.map +1 -0
  105. package/dist/projects/types.d.ts +71 -0
  106. package/dist/projects/types.js +2 -0
  107. package/dist/projects/types.js.map +1 -0
  108. package/dist/proposal/prompt.d.ts +20 -0
  109. package/dist/proposal/prompt.js +64 -0
  110. package/dist/proposal/prompt.js.map +1 -0
  111. package/dist/proposal/run.d.ts +28 -0
  112. package/dist/proposal/run.js +386 -0
  113. package/dist/proposal/run.js.map +1 -0
  114. package/dist/resume/basics.d.ts +4 -0
  115. package/dist/resume/basics.js +33 -0
  116. package/dist/resume/basics.js.map +1 -0
  117. package/dist/resume/find.d.ts +12 -0
  118. package/dist/resume/find.js +27 -0
  119. package/dist/resume/find.js.map +1 -0
  120. package/dist/resume/format.d.ts +3 -0
  121. package/dist/resume/format.js +53 -0
  122. package/dist/resume/format.js.map +1 -0
  123. package/dist/resume/one-page.d.ts +149 -0
  124. package/dist/resume/one-page.js +164 -0
  125. package/dist/resume/one-page.js.map +1 -0
  126. package/dist/resume/prompt.d.ts +10 -0
  127. package/dist/resume/prompt.js +23 -0
  128. package/dist/resume/prompt.js.map +1 -0
  129. package/dist/resume/render.d.ts +8 -0
  130. package/dist/resume/render.js +84 -0
  131. package/dist/resume/render.js.map +1 -0
  132. package/dist/resume/run.d.ts +21 -0
  133. package/dist/resume/run.js +350 -0
  134. package/dist/resume/run.js.map +1 -0
  135. package/dist/resume/skills.d.ts +6 -0
  136. package/dist/resume/skills.js +47 -0
  137. package/dist/resume/skills.js.map +1 -0
  138. package/dist/resume/trace.d.ts +13 -0
  139. package/dist/resume/trace.js +82 -0
  140. package/dist/resume/trace.js.map +1 -0
  141. package/dist/resume/types.d.ts +94 -0
  142. package/dist/resume/types.js +2 -0
  143. package/dist/resume/types.js.map +1 -0
  144. package/dist/resume/verify.d.ts +19 -0
  145. package/dist/resume/verify.js +193 -0
  146. package/dist/resume/verify.js.map +1 -0
  147. package/dist/upwork/profile.d.ts +50 -0
  148. package/dist/upwork/profile.js +302 -0
  149. package/dist/upwork/profile.js.map +1 -0
  150. package/dist/upwork/settings.d.ts +96 -0
  151. package/dist/upwork/settings.js +166 -0
  152. package/dist/upwork/settings.js.map +1 -0
  153. package/dist/util/open-file.d.ts +1 -0
  154. package/dist/util/open-file.js +24 -0
  155. package/dist/util/open-file.js.map +1 -0
  156. package/dist/util/slug.d.ts +2 -0
  157. package/dist/util/slug.js +13 -0
  158. package/dist/util/slug.js.map +1 -0
  159. package/dist/verify/run.d.ts +4 -0
  160. package/dist/verify/run.js +91 -0
  161. package/dist/verify/run.js.map +1 -0
  162. package/package.json +67 -4
  163. package/templates/career.example.yml +63 -0
  164. package/templates/career.private.example.yml +10 -0
  165. package/templates/config.example.yml +16 -0
  166. package/templates/freelance.example.yml +48 -0
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ export declare const DEFAULT_BANNED_PHRASES: string[];
3
+ export declare const RateSchema: z.ZodObject<{
4
+ type: z.ZodDefault<z.ZodEnum<{
5
+ fixed: "fixed";
6
+ hourly: "hourly";
7
+ }>>;
8
+ amount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
9
+ currency: z.ZodDefault<z.ZodString>;
10
+ }, z.core.$strip>;
11
+ export declare const FreelanceConfigSchema: z.ZodObject<{
12
+ profile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
13
+ url: z.ZodDefault<z.ZodString>;
14
+ }, z.core.$loose>]>>;
15
+ rate: z.ZodDefault<z.ZodObject<{
16
+ type: z.ZodDefault<z.ZodEnum<{
17
+ fixed: "fixed";
18
+ hourly: "hourly";
19
+ }>>;
20
+ amount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
21
+ currency: z.ZodDefault<z.ZodString>;
22
+ }, z.core.$strip>>;
23
+ minHourlyRate: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
24
+ availability: z.ZodDefault<z.ZodString>;
25
+ timezone: z.ZodDefault<z.ZodString>;
26
+ timeline: z.ZodDefault<z.ZodString>;
27
+ proposal: z.ZodDefault<z.ZodObject<{
28
+ targetWords: z.ZodDefault<z.ZodNumber>;
29
+ minWords: z.ZodDefault<z.ZodNumber>;
30
+ maxWords: z.ZodDefault<z.ZodNumber>;
31
+ maxChars: z.ZodDefault<z.ZodNumber>;
32
+ includeRate: z.ZodDefault<z.ZodBoolean>;
33
+ includeAvailability: z.ZodDefault<z.ZodBoolean>;
34
+ }, z.core.$strip>>;
35
+ preferredSamples: z.ZodDefault<z.ZodArray<z.ZodString>>;
36
+ bannedPhrases: z.ZodDefault<z.ZodArray<z.ZodString>>;
37
+ }, z.core.$loose>;
38
+ export type FreelanceConfig = z.infer<typeof FreelanceConfigSchema>;
39
+ export type ConfiguredRate = z.infer<typeof RateSchema>;
40
+ export interface EffectiveRate {
41
+ type: 'hourly' | 'fixed';
42
+ amount: number;
43
+ currency: string;
44
+ }
45
+ export interface LoadedFreelanceConfig {
46
+ config: FreelanceConfig;
47
+ path: string | null;
48
+ }
49
+ export declare function loadFreelanceConfig(): LoadedFreelanceConfig;
50
+ export interface ProfileDefaults {
51
+ rate?: EffectiveRate | null;
52
+ hoursPerWeek?: string;
53
+ preferredSamples?: string[];
54
+ }
55
+ export interface ProposalOptions {
56
+ rate?: string | number;
57
+ rateType?: string;
58
+ noRate?: boolean;
59
+ availability?: string;
60
+ timeline?: string;
61
+ profile?: string;
62
+ noProfile?: boolean;
63
+ }
64
+ export interface ProposalSettings {
65
+ targetWords: number;
66
+ minWords: number;
67
+ maxWords: number;
68
+ maxChars: number;
69
+ includeRate: boolean;
70
+ includeAvailability: boolean;
71
+ rate: EffectiveRate | null;
72
+ availability: string;
73
+ timeline: string;
74
+ timezone: string;
75
+ profileUrl: string;
76
+ minHourlyRate: number | null;
77
+ preferredSamples: string[];
78
+ bannedPhrases: string[];
79
+ currency: string;
80
+ }
81
+ export declare function buildProposalSettings(config: FreelanceConfig, opts?: ProposalOptions, profile?: ProfileDefaults | null): ProposalSettings;
82
+ export declare function extractRequiredOpening(body: string): string;
83
+ export declare function parseBudget(value: unknown): {
84
+ min: number;
85
+ max: number;
86
+ raw: string;
87
+ } | null;
88
+ export declare function formatRate(rate: {
89
+ type: 'hourly' | 'fixed';
90
+ amount: number;
91
+ currency?: string;
92
+ } | null | undefined): string;
93
+ export declare function describeBudget(details: {
94
+ budget?: string;
95
+ budgetType?: string;
96
+ }): string;
@@ -0,0 +1,166 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { parse as parseYaml } from 'yaml';
3
+ import { z } from 'zod';
4
+ import { freelanceFile, legacyProposalFile, legacyUpworkFile, } from '../config/paths.js';
5
+ export const DEFAULT_BANNED_PHRASES = [
6
+ 'I hope this message finds you well',
7
+ 'Dear Hiring Manager',
8
+ 'I am excited to apply',
9
+ 'I am the perfect fit',
10
+ 'I would be a great fit',
11
+ 'As an AI language model',
12
+ ];
13
+ export const RateSchema = z.object({
14
+ type: z.enum(['hourly', 'fixed']).default('hourly'),
15
+ amount: z.number().positive().nullable().default(null),
16
+ currency: z.string().default('USD'),
17
+ });
18
+ export const FreelanceConfigSchema = z
19
+ .object({
20
+ profile: z
21
+ .union([
22
+ z.string(),
23
+ z.object({ url: z.string().default('') }).passthrough(),
24
+ ])
25
+ .optional(),
26
+ rate: RateSchema.default({
27
+ type: 'hourly',
28
+ amount: null,
29
+ currency: 'USD',
30
+ }),
31
+ minHourlyRate: z.number().positive().nullable().default(null),
32
+ availability: z.string().default(''),
33
+ timezone: z.string().default(''),
34
+ timeline: z.string().default(''),
35
+ proposal: z
36
+ .object({
37
+ targetWords: z.number().positive().default(180),
38
+ minWords: z.number().positive().default(120),
39
+ maxWords: z.number().positive().default(260),
40
+ maxChars: z.number().positive().default(5000),
41
+ includeRate: z.boolean().default(false),
42
+ includeAvailability: z.boolean().default(true),
43
+ })
44
+ .default({
45
+ targetWords: 180,
46
+ minWords: 120,
47
+ maxWords: 260,
48
+ maxChars: 5000,
49
+ includeRate: false,
50
+ includeAvailability: true,
51
+ }),
52
+ preferredSamples: z.array(z.string()).default([]),
53
+ bannedPhrases: z.array(z.string()).default([]),
54
+ })
55
+ .passthrough();
56
+ export function loadFreelanceConfig() {
57
+ const path = existsSync(freelanceFile)
58
+ ? freelanceFile
59
+ : existsSync(legacyProposalFile)
60
+ ? legacyProposalFile
61
+ : existsSync(legacyUpworkFile)
62
+ ? legacyUpworkFile
63
+ : null;
64
+ if (!path) {
65
+ return { config: FreelanceConfigSchema.parse({}), path: null };
66
+ }
67
+ const raw = parseYaml(readFileSync(path, 'utf8')) ?? {};
68
+ return { config: FreelanceConfigSchema.parse(raw), path };
69
+ }
70
+ export function buildProposalSettings(config, opts = {}, profile) {
71
+ const cliRate = numberOrNull(opts.rate);
72
+ const configRate = numberOrNull(config.rate.amount);
73
+ const profileRate = numberOrNull(profile?.rate?.amount);
74
+ const hourlyRate = cliRate ?? configRate ?? profileRate;
75
+ const rateType = String(opts.rateType || config.rate.type || profile?.rate?.type || 'hourly').toLowerCase();
76
+ const currency = String(config.rate.currency || profile?.rate?.currency || 'USD');
77
+ const configuredProfileUrl = typeof config.profile === 'string'
78
+ ? config.profile
79
+ : config.profile?.url;
80
+ const profileUrl = cleanText(opts.profile) ||
81
+ (!opts.noProfile ? cleanText(configuredProfileUrl) : '');
82
+ const yamlPreferred = config.preferredSamples;
83
+ const preferredSamples = yamlPreferred.length > 0
84
+ ? yamlPreferred
85
+ : (profile?.preferredSamples ?? []);
86
+ return {
87
+ targetWords: config.proposal.targetWords,
88
+ minWords: config.proposal.minWords,
89
+ maxWords: config.proposal.maxWords,
90
+ maxChars: config.proposal.maxChars,
91
+ includeRate: opts.noRate
92
+ ? false
93
+ : Boolean(opts.rate) || config.proposal.includeRate,
94
+ includeAvailability: config.proposal.includeAvailability !== false,
95
+ rate: hourlyRate == null
96
+ ? null
97
+ : {
98
+ type: rateType === 'fixed' ? 'fixed' : 'hourly',
99
+ amount: hourlyRate,
100
+ currency,
101
+ },
102
+ availability: cleanText(opts.availability) ||
103
+ cleanText(config.availability) ||
104
+ cleanText(profile?.hoursPerWeek),
105
+ timeline: cleanText(opts.timeline) || cleanText(config.timeline),
106
+ timezone: cleanText(config.timezone),
107
+ profileUrl,
108
+ minHourlyRate: config.minHourlyRate,
109
+ preferredSamples,
110
+ bannedPhrases: [
111
+ ...new Set([...DEFAULT_BANNED_PHRASES, ...config.bannedPhrases]),
112
+ ],
113
+ currency,
114
+ };
115
+ }
116
+ export function extractRequiredOpening(body) {
117
+ const text = String(body ?? '');
118
+ const match = text.match(/(?:start|begin)\s+(?:(?:your|the)\s+proposal\s+with|with\s+(?:the\s+word|the\s+phrase))\s*(?:the\s+word|the\s+phrase)?\s*["“'‘]?([^"”'’.\n]+)/i);
119
+ if (!match)
120
+ return '';
121
+ return match[1]
122
+ .trim()
123
+ .replace(/^["“'‘]+|["”'’]+$/g, '')
124
+ .replace(/\s+(?:and|then)\b.*$/i, '')
125
+ .replace(/[.,;:]+$/, '')
126
+ .trim();
127
+ }
128
+ export function parseBudget(value) {
129
+ const text = String(value ?? '').replace(/,/g, '');
130
+ const amounts = [...text.matchAll(/\$?\s*(\d+(?:\.\d+)?)/g)].map((match) => Number(match[1]));
131
+ if (amounts.length === 0)
132
+ return null;
133
+ return {
134
+ min: Math.min(...amounts),
135
+ max: Math.max(...amounts),
136
+ raw: String(value),
137
+ };
138
+ }
139
+ export function formatRate(rate) {
140
+ if (!rate || rate.amount == null)
141
+ return '';
142
+ const symbol = rate.currency === 'EUR' ? '€' : rate.currency === 'GBP' ? '£' : '$';
143
+ const amount = Number(rate.amount).toLocaleString('en-US', {
144
+ maximumFractionDigits: 2,
145
+ });
146
+ return rate.type === 'fixed'
147
+ ? `${symbol}${amount} fixed`
148
+ : `${symbol}${amount}/hr`;
149
+ }
150
+ export function describeBudget(details) {
151
+ if (!details.budget)
152
+ return '';
153
+ return details.budgetType === 'fixed'
154
+ ? `${details.budget} fixed`
155
+ : `${details.budget}/hr`;
156
+ }
157
+ function numberOrNull(value) {
158
+ if (value == null || value === '')
159
+ return null;
160
+ const number = Number(value);
161
+ return Number.isFinite(number) && number > 0 ? number : null;
162
+ }
163
+ function cleanText(value) {
164
+ return typeof value === 'string' ? value.trim() : '';
165
+ }
166
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/upwork/settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function openFile(path: string): boolean;
@@ -0,0 +1,24 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ export function openFile(path) {
3
+ const candidates = process.platform === 'darwin'
4
+ ? [['open', [path]]]
5
+ : process.platform === 'win32'
6
+ ? [['cmd', ['/c', 'start', '', path]]]
7
+ : [
8
+ ['xdg-open', [path]],
9
+ ['gio', ['open', path]],
10
+ ['kde-open', [path]],
11
+ ['gnome-open', [path]],
12
+ ['wslview', [path]],
13
+ ];
14
+ for (const [command, args] of candidates) {
15
+ const result = spawnSync(command, args, {
16
+ stdio: 'ignore',
17
+ timeout: 5000,
18
+ });
19
+ if (!result.error && result.status === 0)
20
+ return true;
21
+ }
22
+ return false;
23
+ }
24
+ //# sourceMappingURL=open-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-file.js","sourceRoot":"","sources":["../../src/util/open-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,UAAU,GACd,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;YACtC,CAAC,CAAC;gBACE,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACvB,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC;gBACtB,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;aACpB,CAAC;IAEV,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE;YACtC,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function slugify(value: unknown): string;
2
+ export declare function truncate(value: unknown, length: number): string;
@@ -0,0 +1,13 @@
1
+ export function slugify(value) {
2
+ return String(value ?? '')
3
+ .toLowerCase()
4
+ .normalize('NFKD')
5
+ .replace(/[^a-z0-9]+/g, '-')
6
+ .replace(/^-+|-+$/g, '')
7
+ .slice(0, 70);
8
+ }
9
+ export function truncate(value, length) {
10
+ const text = String(value ?? '').replace(/\s+/g, ' ').trim();
11
+ return text.length > length ? `${text.slice(0, length - 1)}…` : text;
12
+ }
13
+ //# sourceMappingURL=slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/util/slug.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,WAAW,EAAE;SACb,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,MAAc;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,OAAO,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export interface VerifyRunOptions {
2
+ input: string;
3
+ }
4
+ export declare function runVerify(options: VerifyRunOptions): Promise<void>;
@@ -0,0 +1,91 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { loadCareer } from '../career/load.js';
4
+ import { loadConfig } from '../config/config.js';
5
+ import { resolveOutputDir } from '../config/paths.js';
6
+ import { getPdfPageCount } from '../resume/render.js';
7
+ import { verifyTailoredResume } from '../resume/verify.js';
8
+ import { slugify } from '../util/slug.js';
9
+ export async function runVerify(options) {
10
+ const { config } = loadConfig();
11
+ const resolved = resolveResumeFiles(options.input, resolveOutputDir(config.output.dir, undefined));
12
+ const resume = JSON.parse(readFileSync(resolved.resumePath, 'utf8'));
13
+ const trace = JSON.parse(readFileSync(resolved.tracePath, 'utf8'));
14
+ const { career } = loadCareer();
15
+ const verification = await verifyTailoredResume({
16
+ resume,
17
+ trace,
18
+ career,
19
+ });
20
+ for (const warning of verification.warnings) {
21
+ console.warn(`dossier: warning: ${warning}`);
22
+ }
23
+ if (resolved.pdfPath && existsSync(resolved.pdfPath)) {
24
+ const pages = await getPdfPageCount(resolved.pdfPath);
25
+ if (pages !== 1) {
26
+ verification.errors.push(`PDF is ${pages} pages, expected 1`);
27
+ }
28
+ else {
29
+ console.log('dossier: PDF is 1 page');
30
+ }
31
+ }
32
+ for (const error of verification.errors) {
33
+ console.error(`dossier: error: ${error}`);
34
+ }
35
+ console.log(`dossier: ${verification.errors.length} error(s), ${verification.warnings.length} warning(s), ${verification.gaps.length} gap(s)`);
36
+ if (verification.gaps.length > 0) {
37
+ console.log('dossier: gaps:');
38
+ for (const gap of verification.gaps)
39
+ console.log(` - ${gap}`);
40
+ }
41
+ if (verification.errors.length > 0) {
42
+ throw new Error('Resume verification failed.');
43
+ }
44
+ }
45
+ function resolveResumeFiles(input, outputDir) {
46
+ const direct = resolve(process.cwd(), input);
47
+ if (existsSync(direct)) {
48
+ if (direct.toLowerCase().endsWith('.pdf')) {
49
+ return {
50
+ resumePath: direct.replace(/\.pdf$/i, '.json'),
51
+ tracePath: direct.replace(/\.pdf$/i, '.trace.json'),
52
+ pdfPath: direct,
53
+ };
54
+ }
55
+ if (direct.toLowerCase().endsWith('.json')) {
56
+ const tracePath = direct.endsWith('.trace.json')
57
+ ? direct
58
+ : direct.replace(/\.json$/i, '.trace.json');
59
+ const resumePath = direct.endsWith('.trace.json')
60
+ ? direct.replace(/\.trace\.json$/i, '.json')
61
+ : direct;
62
+ return {
63
+ resumePath,
64
+ tracePath,
65
+ pdfPath: resumePath.replace(/\.json$/i, '.pdf'),
66
+ };
67
+ }
68
+ return {
69
+ resumePath: join(direct, 'resume.json'),
70
+ tracePath: join(direct, 'trace.json'),
71
+ pdfPath: join(direct, 'resume.pdf'),
72
+ };
73
+ }
74
+ const slug = slugify(input.replace(/\.(json|pdf)$/i, ''));
75
+ const candidates = [
76
+ resolve(process.cwd(), `resume-${slug}.json`),
77
+ resolve(outputDir, `resume-${slug}.json`),
78
+ resolve(process.cwd(), input.endsWith('.json') ? input : `${input}.json`),
79
+ ];
80
+ for (const resumePath of candidates) {
81
+ if (!existsSync(resumePath))
82
+ continue;
83
+ return {
84
+ resumePath,
85
+ tracePath: resumePath.replace(/\.json$/i, '.trace.json'),
86
+ pdfPath: resumePath.replace(/\.json$/i, '.pdf'),
87
+ };
88
+ }
89
+ throw new Error(`Resume not found: ${input}. Pass a .json/.pdf path or a resume slug.`);
90
+ }
91
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/verify/run.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@rwendell/dossier",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Grounded resume, application answer, and Upwork proposal generation from a local career dossier.",
5
+ "type": "module",
6
+ "bin": {
7
+ "dossier": "dist/cli/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "agents",
12
+ "templates",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json",
21
+ "dev": "tsx src/cli/index.ts",
22
+ "test": "vitest run",
23
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
24
+ "prepack": "aube run build",
25
+ "prepublishOnly": "aube run typecheck && aube run test"
26
+ },
27
+ "keywords": [
28
+ "resume",
29
+ "json-resume",
30
+ "upwork",
31
+ "proposal",
32
+ "freelance",
33
+ "opencode"
34
+ ],
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/rwendell/dossier.git"
39
+ },
40
+ "homepage": "https://github.com/rwendell/dossier#readme",
41
+ "bugs": {
42
+ "url": "https://github.com/rwendell/dossier/issues"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@jsonresume/schema": "1.7.0",
49
+ "cheerio": "^1.2.0",
50
+ "clipboardy": "^5.3.2",
51
+ "env-paths": "^4.0.0",
52
+ "html-to-text": "^10.0.1",
53
+ "jsonresume-theme-straightforward": "0.5.0",
54
+ "pdf-lib": "1.17.1",
55
+ "puppeteer": "^24.43.1",
56
+ "resumed": "6.1.0",
57
+ "yaml": "^2.9.1",
58
+ "yargs": "^18.2.0",
59
+ "zod": "^4.6.7"
60
+ },
61
+ "devDependencies": {
62
+ "@types/html-to-text": "^9.0.4",
63
+ "@types/node": "^26.6.6",
64
+ "@types/yargs": "^17.0.35",
65
+ "tsx": "^4.23.15",
66
+ "typescript": "^7.0.2",
67
+ "vitest": "^5.1.0"
68
+ }
69
+ }
@@ -0,0 +1,63 @@
1
+ # Dossier career fact bank.
2
+ # Keep public, non-PII facts here. Put name/contact/location in
3
+ # career.private.yml.
4
+
5
+ version: 1
6
+ basics:
7
+ profiles:
8
+ - network: GitHub
9
+ username: your-handle
10
+ url: https://github.com/your-handle
11
+
12
+ summary: >-
13
+ One or two sentences describing the kind of work you do and the value you
14
+ bring. The proposal agent will use this as framing, not as evidence.
15
+
16
+ generic:
17
+ title: Software Engineer
18
+ publishAs: software-engineer
19
+ description: >-
20
+ Build a general-purpose resume from the strongest evidence in this fact
21
+ bank. Prefer quantified outcomes and recent, relevant work.
22
+
23
+ roles:
24
+ - id: example-company
25
+ company: Example Company
26
+ position: Software Engineer
27
+ startDate: 2022-01
28
+ endDate: 2025-01
29
+ summary: Built and maintained customer-facing systems.
30
+ achievements:
31
+ - id: example-delivery
32
+ text: Led delivery of a production feature from requirements through deployment and support.
33
+ skills: [delivery, api-design, testing]
34
+ strength: 5
35
+
36
+ projects:
37
+ - id: example-project
38
+ name: Example Project
39
+ repo: your-handle/example-project
40
+ url: https://github.com/your-handle/example-project
41
+ description: A short description of the project.
42
+ highlights:
43
+ - id: example-project-highlight
44
+ text: Built a multi-user application with authentication and real-time data.
45
+ skills: [typescript, full-stack]
46
+
47
+ education:
48
+ - id: example-school
49
+ institution: Example University
50
+ area: Computer Science
51
+ studyType: Bachelor of Science
52
+ startDate: 2014
53
+ endDate: 2018
54
+
55
+ skills:
56
+ - name: TypeScript
57
+ - name: SQL
58
+
59
+ constraints:
60
+ doNotClaim:
61
+ - People management unless explicitly listed
62
+
63
+ transferable: []
@@ -0,0 +1,10 @@
1
+ # Private overlay for the career fact bank. This file is not published by the
2
+ # site repo. Put PII here.
3
+ basics:
4
+ name: Your Name
5
+ email: you@example.com
6
+ phone: (555) 555-5555
7
+ location:
8
+ city: Your City
9
+ region: Your State
10
+ countryCode: US
@@ -0,0 +1,16 @@
1
+ # Dossier machine configuration.
2
+ output:
3
+ # Empty means the current working directory.
4
+ dir: ""
5
+ # Open the generated PDF when tailoring finishes.
6
+ open: true
7
+
8
+ cache:
9
+ # auto | dev | release
10
+ mode: "auto"
11
+ # Optional explicit cache directory; overrides mode.
12
+ dir: ""
13
+
14
+ theme: "jsonresume-theme-straightforward"
15
+ provider: "auto"
16
+ editor: ""
@@ -0,0 +1,48 @@
1
+ # Dossier freelance proposal defaults.
2
+ #
3
+ # Copy this file to ~/.local/share/dossier/freelance.yml and edit it. CLI flags
4
+ # override these values.
5
+
6
+ # Public Upwork profile. When set, public values fill name, title, location,
7
+ # rate, skills, and portfolio; hours per week is used when present. Values below
8
+ # and CLI flags override.
9
+ profile:
10
+ url: "" # e.g. https://www.upwork.com/freelancers/~01fbe3cf033cea5004
11
+
12
+ # Your standard rate. Used in proposals only when includeRate is true, and for
13
+ # the bid/no-bid warning against a job's stated budget. Leave amount null to
14
+ # use the public profile rate.
15
+ rate:
16
+ type: hourly # hourly | fixed
17
+ amount: null # e.g. 85
18
+ currency: USD
19
+
20
+ # Warn when an hourly job's maximum budget is below this. Leave null to disable.
21
+ minHourlyRate: null
22
+
23
+ # Logistics included when includeAvailability is true.
24
+ availability: "" # e.g. "20-30 hrs/week; UK overlap until 6pm GMT"
25
+ timezone: "" # e.g. "US Eastern"
26
+ timeline: "" # e.g. "2 weeks"; --timeline overrides
27
+
28
+ proposal:
29
+ targetWords: 180
30
+ minWords: 120
31
+ maxWords: 260
32
+ maxChars: 5000
33
+ includeRate: false
34
+ includeAvailability: true
35
+
36
+ # Soft hint: evidence ids or project names to prefer when relevant. The agent
37
+ # still chooses the final `samples` based on the job; this list does not filter
38
+ # or rank candidates.
39
+ preferredSamples: []
40
+
41
+ # The verifier warns when a proposal contains these generic phrases. The
42
+ # built-in defaults are always included.
43
+ bannedPhrases:
44
+ - "I hope this message finds you well"
45
+ - "Dear Hiring Manager"
46
+ - "I am excited to apply"
47
+ - "I am the perfect fit"
48
+ - "I would be a great fit"