← Files Career Command CenterARCHIVED FILE
assets/macos-app/Sources/AppStore.swift
47.8 KB · Oct 2, 2026 · 00:29 UTC
import AppKit
import Foundation
import SwiftUI
@MainActor
final class AppStore: ObservableObject {
@Published var config = AppConfig()
@Published var state = CommandCenterState()
@Published var selectedSection: AppSection = .toApply
@Published var selectedLeadID: String?
@Published var searchText = ""
@Published var documentItems: [DocumentItem] = []
@Published var questionBank = PersonalizedQuestionBank()
@Published var selectedQuestionID: String?
@Published var toastMessage = ""
@Published var errorMessage = ""
@Published var isBusy = false
private(set) var workspaceURL: URL
private let fileManager = FileManager.default
private let encoder: JSONEncoder
private let decoder = JSONDecoder()
private let previewMode: Bool
static let workspacePreferenceKey = "CareerCommandCenter.workspacePath"
init(workspaceOverride: URL? = nil, preview: Bool = false) {
encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
previewMode = preview
workspaceURL = workspaceOverride ?? Self.resolveWorkspace()
if preview {
config = Self.previewConfig(workspaceURL: workspaceURL)
state = Self.previewState()
documentItems = Self.previewDocuments(workspaceURL: workspaceURL)
questionBank = Self.previewQuestionBank()
selectedLeadID = state.leads.first?.id
selectedQuestionID = questionBank.questions.first?.id
return
}
ensureWorkspaceStructure()
loadConfig()
loadState()
refreshDocuments()
loadQuestions()
}
var stateURL: URL {
workspaceURL.appendingPathComponent("State/cv_command_center_state.json")
}
var configURL: URL {
workspaceURL.appendingPathComponent("Config/command_center_config.json")
}
var automationStatusURL: URL {
workspaceURL.appendingPathComponent("Automation/automation_status.json")
}
var questionsURL: URL {
workspaceURL.appendingPathComponent("Evidence_Bank/personalized_questions.json")
}
var selectedLead: LeadRecord? {
let source = selectedSection == .deleted ? state.deletedLeads : state.leads
return source.first { $0.id == selectedLeadID }
}
var visibleLeads: [LeadRecord] {
let source: [LeadRecord]
if selectedSection == .deleted {
source = state.deletedLeads
} else if let status = selectedSection.leadStatus {
source = state.leads.filter { $0.status == status }
} else {
source = []
}
let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
let filtered = query.isEmpty ? source : source.filter { lead in
([lead.title, lead.organization, lead.location, lead.type] +
lead.summary + lead.requirements + lead.matchStrengths + lead.fitGaps)
.joined(separator: " ")
.lowercased()
.contains(query)
}
return filtered.sorted { lhs, rhs in
let left = lhs.score ?? -1
let right = rhs.score ?? -1
if left != right { return left > right }
return lhs.updatedAt > rhs.updatedAt
}
}
var selectedQuestion: EvidenceQuestion? {
questionBank.questions.first { $0.id == selectedQuestionID }
}
var questionsNeedingAnswer: [EvidenceQuestion] {
sortedQuestions(questionBank.questions.filter { $0.status.needsUserAnswer })
}
var questionsAwaitingReview: [EvidenceQuestion] {
sortedQuestions(questionBank.questions.filter { $0.status.awaitsCodexReview })
}
var questionHistory: [EvidenceQuestion] {
sortedQuestions(questionBank.questions.filter { $0.status.isHistory })
}
var actionableQuestionCount: Int {
questionsNeedingAnswer.count + questionsAwaitingReview.count
}
var questionSidebarCount: Int {
max(actionableQuestionCount, questionBank.auditStatus.needsAudit ? 1 : 0)
}
func count(for section: AppSection) -> Int {
if section == .deleted { return state.deletedLeads.count }
if section == .questions { return questionSidebarCount }
guard let status = section.leadStatus else { return 0 }
return state.leads.filter { $0.status == status }.count
}
func reload() {
loadConfig()
loadState()
refreshDocuments()
loadQuestions()
showToast("Workspace refreshed")
}
func saveConfig(markAutomationDirty: Bool = false) {
if markAutomationDirty {
config.automation.needsCodexSync = true
}
config.updatedAt = Self.timestamp()
do {
try atomicWrite(config, to: configURL)
} catch {
errorMessage = "Could not save settings: \(error.localizedDescription)"
}
}
func finishOnboarding() {
config.onboardingCompleted = true
config.onboardingCompletedAt = Self.timestamp()
config.workspacePath = workspaceURL.path
config.automation.needsCodexSync = true
UserDefaults.standard.set(workspaceURL.path, forKey: Self.workspacePreferenceKey)
saveConfig(markAutomationDirty: true)
writeIntakeSummary()
markQuestionAuditStale("Initial intake is ready for a source-specific evidence audit.")
showToast("Setup saved. Return to Codex to audit the files and generate your questions.")
}
func saveEvidenceAnswers() {
saveConfig()
writeIntakeSummary()
markQuestionAuditStale("Background answers changed after the previous evidence audit.")
showToast("Evidence answers saved")
}
func refreshQuestions(showConfirmation: Bool = true) {
if previewMode { return }
loadQuestions()
if showConfirmation { showToast("Questions refreshed") }
}
func saveQuestionResponse(_ questionID: String, answer: String, status: EvidenceQuestionStatus) {
guard [.answered, .unableToVerify, .notApplicable].contains(status),
let index = questionBank.questions.firstIndex(where: { $0.id == questionID }) else { return }
let cleaned = answer.trimmingCharacters(in: .whitespacesAndNewlines)
if status == .answered && cleaned.isEmpty {
errorMessage = "Write an answer before marking this question ready for review."
return
}
questionBank.questions[index].answer = cleaned
questionBank.questions[index].status = status
questionBank.questions[index].answeredAt = Self.timestamp()
questionBank.questions[index].reviewedAt = ""
questionBank.questions[index].reviewNote = ""
saveQuestions()
switch status {
case .answered: showToast("Answer saved for Codex review")
case .unableToVerify: showToast("Evidence boundary recorded")
case .notApplicable: showToast("Question marked not applicable")
default: break
}
}
func reopenQuestion(_ questionID: String) {
guard let index = questionBank.questions.firstIndex(where: { $0.id == questionID }) else { return }
questionBank.questions[index].status = .open
questionBank.questions[index].reviewedAt = ""
questionBank.questions[index].reviewNote = ""
saveQuestions()
showToast("Question reopened")
}
func restartOnboarding() {
config.onboardingCompleted = false
config.onboardingCompletedAt = ""
saveConfig()
}
func setWorkspace(_ url: URL) {
workspaceURL = url.standardizedFileURL
config.workspacePath = workspaceURL.path
UserDefaults.standard.set(workspaceURL.path, forKey: Self.workspacePreferenceKey)
ensureWorkspaceStructure()
loadConfig()
loadState()
refreshDocuments()
loadQuestions()
}
func chooseWorkspaceFolder() {
let panel = NSOpenPanel()
panel.title = "Choose a Career Command Center workspace"
panel.prompt = "Use Folder"
panel.canChooseDirectories = true
panel.canChooseFiles = false
panel.canCreateDirectories = true
panel.allowsMultipleSelection = false
panel.directoryURL = workspaceURL.deletingLastPathComponent()
guard panel.runModal() == .OK, let url = panel.url else { return }
setWorkspace(url)
}
func createWorkspaceInDocuments() {
let url = fileManager.homeDirectoryForCurrentUser
.appendingPathComponent("Documents/Career Command Center", isDirectory: true)
setWorkspace(url)
}
func markApplied(_ leadID: String) {
mutateLead(leadID) { lead in
lead.status = .applied
lead.remove("archived_at")
lead.remove("previous_status")
lead.set("applied_at", Self.timestamp())
lead.set("updated_at", Self.timestamp())
}
selectedSection = .applied
showToast("Marked as applied")
}
func moveToApply(_ leadID: String) {
mutateLead(leadID) { lead in
lead.status = .toApply
lead.remove("applied_at")
lead.remove("archived_at")
lead.remove("previous_status")
lead.set("updated_at", Self.timestamp())
}
selectedSection = .toApply
showToast("Moved to To Apply")
}
func saveForLater(_ leadID: String) {
mutateLead(leadID) { lead in
lead.status = .monitor
lead.remove("applied_at")
lead.remove("archived_at")
lead.remove("previous_status")
lead.set("updated_at", Self.timestamp())
}
selectedSection = .monitor
showToast("Saved for later")
}
func archive(_ leadID: String) {
mutateLead(leadID) { lead in
lead.previousStatus = lead.status
lead.status = .archived
lead.set("archived_at", Self.timestamp())
lead.set("updated_at", Self.timestamp())
}
selectedLeadID = nil
showToast("Moved to Archive")
}
func restoreArchived(_ leadID: String) {
var restoredStatus = LeadStatus.toApply
mutateLead(leadID) { lead in
let destination = lead.previousStatus == .archived || lead.previousStatus == .deleted
? LeadStatus.toApply
: lead.previousStatus
restoredStatus = destination
lead.status = destination
lead.remove("archived_at")
lead.remove("previous_status")
lead.set("updated_at", Self.timestamp())
}
selectedSection = section(for: restoredStatus)
showToast("Restored from Archive")
}
func deleteLead(_ leadID: String) {
guard let index = state.leads.firstIndex(where: { $0.id == leadID }) else { return }
var lead = state.leads.remove(at: index)
let deletedAt = Self.timestamp()
lead.previousStatus = lead.status
lead.status = .deleted
lead.set("deleted_at", deletedAt)
lead.set("updated_at", deletedAt)
state.deletedLeads.removeAll { $0.id == lead.id }
state.deletedLeads.append(lead)
addTombstone(for: lead, deletedAt: deletedAt)
saveState()
selectedLeadID = nil
showToast("Moved to Recently Deleted")
}
func restoreDeleted(_ leadID: String) {
guard let index = state.deletedLeads.firstIndex(where: { $0.id == leadID }) else { return }
var lead = state.deletedLeads.remove(at: index)
let destination = lead.previousStatus == .deleted ? LeadStatus.toApply : lead.previousStatus
lead.status = destination
lead.remove("deleted_at")
lead.remove("previous_status")
lead.set("updated_at", Self.timestamp())
state.leads.removeAll { $0.id == lead.id }
state.leads.append(lead)
state.tombstones.removeAll { !$0.dedupeKeys.isDisjoint(with: lead.dedupeKeys) }
saveState()
selectedSection = section(for: destination)
selectedLeadID = lead.id
showToast("Opportunity restored")
}
func permanentlyDelete(_ leadID: String) {
guard let lead = state.deletedLeads.first(where: { $0.id == leadID }) else { return }
state.deletedLeads.removeAll { $0.id == leadID }
if !state.tombstones.contains(where: { !$0.dedupeKeys.isDisjoint(with: lead.dedupeKeys) }) {
addTombstone(for: lead, deletedAt: lead.deletedAt.isEmpty ? Self.timestamp() : lead.deletedAt)
}
saveState()
selectedLeadID = nil
showToast("Posting details deleted; dedupe marker retained")
}
func updateUserNotes(_ leadID: String, notes: String) {
if selectedSection == .deleted {
guard let index = state.deletedLeads.firstIndex(where: { $0.id == leadID }) else { return }
state.deletedLeads[index].set("user_notes", notes)
saveState()
return
}
mutateLead(leadID) { lead in
lead.set("user_notes", notes)
lead.set("updated_at", Self.timestamp())
}
}
func importDocuments(category: DocumentCategory) {
let panel = NSOpenPanel()
panel.title = "Add \(category.rawValue)"
panel.prompt = "Import"
panel.canChooseFiles = true
panel.canChooseDirectories = true
panel.allowsMultipleSelection = true
guard panel.runModal() == .OK else { return }
isBusy = true
defer { isBusy = false }
let destination = documentDirectory(for: category)
do {
try fileManager.createDirectory(at: destination, withIntermediateDirectories: true)
for source in panel.urls {
try copyItemUniquely(from: source, to: destination)
}
refreshDocuments()
markQuestionAuditStale("New or changed documents were imported after the previous evidence audit.")
showToast("Documents imported")
} catch {
errorMessage = "Could not import documents: \(error.localizedDescription)"
}
}
func importProjectFolder() {
let panel = NSOpenPanel()
panel.title = "Add project reports and source material"
panel.prompt = "Import Project"
panel.canChooseDirectories = true
panel.canChooseFiles = true
panel.allowsMultipleSelection = true
guard panel.runModal() == .OK else { return }
let destination = workspaceURL.appendingPathComponent("Projects", isDirectory: true)
do {
try fileManager.createDirectory(at: destination, withIntermediateDirectories: true)
for source in panel.urls {
try copyItemUniquely(from: source, to: destination)
}
markQuestionAuditStale("New or changed project material was imported after the previous evidence audit.")
showToast("Project material imported")
} catch {
errorMessage = "Could not import project material: \(error.localizedDescription)"
}
}
func refreshDocuments() {
var items: [DocumentItem] = []
for category in DocumentCategory.allCases {
let directory = documentDirectory(for: category)
guard let enumerator = fileManager.enumerator(
at: directory,
includingPropertiesForKeys: [.contentModificationDateKey, .fileSizeKey, .isRegularFileKey],
options: [.skipsHiddenFiles]
) else { continue }
for case let url as URL in enumerator {
guard let values = try? url.resourceValues(forKeys: [.contentModificationDateKey, .fileSizeKey, .isRegularFileKey]),
values.isRegularFile == true else { continue }
items.append(
DocumentItem(
url: url,
category: category,
modifiedAt: values.contentModificationDate ?? .distantPast,
byteCount: Int64(values.fileSize ?? 0)
)
)
}
}
documentItems = items.sorted { $0.modifiedAt > $1.modifiedAt }
}
func documentCount(for category: DocumentCategory) -> Int {
documentItems.filter { $0.category == category }.count
}
func revealWorkspace() {
NSWorkspace.shared.activateFileViewerSelecting([workspaceURL])
}
func revealDocumentInbox() {
let url = workspaceURL.appendingPathComponent("Documents", isDirectory: true)
NSWorkspace.shared.activateFileViewerSelecting([url])
}
func revealProjects() {
let url = workspaceURL.appendingPathComponent("Projects", isDirectory: true)
NSWorkspace.shared.activateFileViewerSelecting([url])
}
func open(path: String) {
guard !path.isEmpty else { return }
NSWorkspace.shared.open(URL(fileURLWithPath: path))
}
func reveal(path: String) {
guard !path.isEmpty else { return }
NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: path)])
}
func openWeb(_ value: String) {
guard let url = URL(string: value), !value.isEmpty else { return }
NSWorkspace.shared.open(url)
}
func openQuestionSource(_ source: EvidenceQuestionSource) {
let path = NSString(string: source.path).expandingTildeInPath
let url = path.hasPrefix("/")
? URL(fileURLWithPath: path)
: workspaceURL.appendingPathComponent(path)
guard fileManager.fileExists(atPath: url.path) else {
errorMessage = "The cited source could not be found at \(source.path)."
return
}
NSWorkspace.shared.open(url)
}
func copyCodexRequest(_ text: String) {
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(text, forType: .string)
if let url = URL(string: "codex://") {
NSWorkspace.shared.open(url)
}
showToast("Codex request copied")
}
func automationSyncRequest() -> String {
"Sync my Career Command Center automation with the settings in \(configURL.path)."
}
func runSearchRequest() -> String {
"Run my Career Command Center job and PhD search now using \(configURL.path)."
}
func questionGenerationRequest() -> String {
"Audit my Career Command Center CVs, project files, transcripts, and intake answers. Generate only source-specific, high-impact evidence questions in \(questionsURL.path) using the plugin question standard and question_cli.py."
}
func questionReviewRequest() -> String {
"Review my answered Career Command Center evidence questions in \(questionsURL.path). Update the verified evidence ledger and approved evidence, resolve each reviewed response through question_cli.py, and generate only necessary cited follow-ups."
}
func showToast(_ message: String) {
toastMessage = message
Task { @MainActor in
try? await Task.sleep(for: .seconds(2.4))
if toastMessage == message { toastMessage = "" }
}
}
private func mutateLead(_ leadID: String, mutation: (inout LeadRecord) -> Void) {
guard let index = state.leads.firstIndex(where: { $0.id == leadID }) else { return }
mutation(&state.leads[index])
saveState()
}
private func section(for status: LeadStatus) -> AppSection {
switch status {
case .toApply: return .toApply
case .monitor: return .monitor
case .applied: return .applied
case .archived: return .archive
case .deleted: return .deleted
}
}
private func addTombstone(for lead: LeadRecord, deletedAt: String) {
let tombstone = LeadTombstone(lead: lead, deletedAt: deletedAt)
state.tombstones.removeAll { !$0.dedupeKeys.isDisjoint(with: tombstone.dedupeKeys) }
state.tombstones.append(tombstone)
}
private func saveState() {
state.version = max(state.version, 3)
state.updatedAt = Self.timestamp()
do {
try atomicWrite(state, to: stateURL)
} catch {
errorMessage = "Could not save application state: \(error.localizedDescription)"
}
}
private func saveQuestions() {
questionBank.version = 1
questionBank.updatedAt = Self.timestamp()
do {
try atomicWrite(questionBank, to: questionsURL)
} catch {
errorMessage = "Could not save evidence questions: \(error.localizedDescription)"
}
}
private func markQuestionAuditStale(_ note: String) {
questionBank.auditStatus = questionBank.generationID.isEmpty ? .notStarted : .needsRefresh
questionBank.sourceChangeNote = note
saveQuestions()
}
private func loadQuestions() {
do {
if fileManager.fileExists(atPath: questionsURL.path) {
let data = try Data(contentsOf: questionsURL)
questionBank = try decoder.decode(PersonalizedQuestionBank.self, from: data)
} else {
questionBank = PersonalizedQuestionBank()
saveQuestions()
}
markQuestionAuditStaleIfSourcesChanged()
if !questionBank.questions.contains(where: { $0.id == selectedQuestionID }) {
selectedQuestionID = questionsNeedingAnswer.first?.id
?? questionsAwaitingReview.first?.id
?? questionHistory.first?.id
}
} catch {
errorMessage = "Could not read evidence questions: \(error.localizedDescription)"
questionBank = PersonalizedQuestionBank()
selectedQuestionID = nil
}
}
private func markQuestionAuditStaleIfSourcesChanged() {
guard questionBank.auditStatus == .current,
let auditedAt = ISO8601DateFormatter().date(from: questionBank.generatedAt),
let newestSource = newestEvidenceSourceDate(),
newestSource.timeIntervalSince(auditedAt) > 2 else { return }
questionBank.auditStatus = .needsRefresh
questionBank.sourceChangeNote = "Source files changed after the previous evidence audit."
saveQuestions()
}
private func newestEvidenceSourceDate() -> Date? {
let roots = [
workspaceURL.appendingPathComponent("Documents", isDirectory: true),
workspaceURL.appendingPathComponent("Projects", isDirectory: true),
]
var newest: Date?
for root in roots {
guard let enumerator = fileManager.enumerator(
at: root,
includingPropertiesForKeys: [.contentModificationDateKey, .isRegularFileKey],
options: [.skipsHiddenFiles]
) else { continue }
for case let url as URL in enumerator {
guard let values = try? url.resourceValues(
forKeys: [.contentModificationDateKey, .isRegularFileKey]
), values.isRegularFile == true, let modifiedAt = values.contentModificationDate else { continue }
if newest == nil || modifiedAt > newest! { newest = modifiedAt }
}
}
let intakeURL = workspaceURL.appendingPathComponent("Evidence_Bank/intake_answers.md")
if let values = try? intakeURL.resourceValues(forKeys: [.contentModificationDateKey]),
let modifiedAt = values.contentModificationDate,
newest == nil || modifiedAt > newest! {
newest = modifiedAt
}
return newest
}
private func sortedQuestions(_ questions: [EvidenceQuestion]) -> [EvidenceQuestion] {
let rank: [EvidenceQuestionPriority: Int] = [.critical: 0, .high: 1, .medium: 2]
return questions.sorted { lhs, rhs in
let left = rank[lhs.priority] ?? 3
let right = rank[rhs.priority] ?? 3
if left != right { return left < right }
return lhs.generatedAt > rhs.generatedAt
}
}
private func loadState() {
do {
if fileManager.fileExists(atPath: stateURL.path) {
let data = try Data(contentsOf: stateURL)
state = try decoder.decode(CommandCenterState.self, from: data)
} else {
state = CommandCenterState()
saveState()
}
migrateStateSchema()
if selectedLeadID == nil {
selectedLeadID = visibleLeads.first?.id
}
} catch {
errorMessage = "Could not read application state: \(error.localizedDescription)"
state = CommandCenterState()
}
}
private func loadConfig() {
do {
if fileManager.fileExists(atPath: configURL.path) {
let data = try Data(contentsOf: configURL)
config = try decoder.decode(AppConfig.self, from: data)
if config.version < 2 {
config.version = 2
saveConfig()
}
} else {
config = AppConfig()
config.workspacePath = workspaceURL.path
prefillProfileFromEvidenceBank()
detectExistingAutomation()
saveConfig()
}
} catch {
errorMessage = "Could not read settings: \(error.localizedDescription)"
config = AppConfig()
config.workspacePath = workspaceURL.path
}
}
private func migrateStateSchema() {
var changed = state.version < 3
for index in state.leads.indices {
let old = state.leads[index].string("status")?.lowercased()
if old == "hidden" || old == "dismissed" {
state.leads[index].status = .archived
state.leads[index].set("migrated_from_status", old ?? "hidden")
changed = true
} else if old == "manual_check" || old == "manual check" {
state.leads[index].status = .toApply
state.leads[index].set("migrated_from_status", old ?? "manual_check")
changed = true
}
if migrateAssessment(&state.leads[index]) {
changed = true
}
}
for index in state.deletedLeads.indices {
if migrateAssessment(&state.deletedLeads[index]) {
changed = true
}
}
if changed { saveState() }
}
private func migrateAssessment(_ lead: inout LeadRecord) -> Bool {
let structuredKeys = [
"match_strengths",
"fit_gaps",
"eligibility_constraints",
"application_requirements",
"search_notes"
]
let hadStructuredAssessment = structuredKeys.contains { lead.raw[$0] != nil }
let previousVersion = lead.raw["assessment_schema_version"]?.intValue ?? 0
var strengths = lead.stringArray("match_strengths")
var gaps = lead.stringArray("fit_gaps")
var eligibility = lead.stringArray("eligibility_constraints")
var application = lead.stringArray("application_requirements")
var notes = lead.stringArray("search_notes")
if strengths.isEmpty, !lead.rationale.isEmpty {
strengths = [lead.rationale]
}
if !hadStructuredAssessment, !lead.concerns.isEmpty {
for rawClause in assessmentClauses(lead.concerns) {
let clause = cleanedAssessmentClause(rawClause)
guard !clause.isEmpty else { continue }
switch assessmentBucket(for: clause) {
case .application: application.append(clause)
case .eligibility: eligibility.append(clause)
case .note: notes.append(clause)
case .gap: gaps.append(clause)
}
}
}
var correctedGaps: [String] = []
for gap in gaps {
if assessmentBucket(for: gap) == .application {
application.append(gap)
} else {
correctedGaps.append(gap)
}
}
strengths = uniqueAssessmentItems(strengths)
gaps = uniqueAssessmentItems(correctedGaps)
eligibility = uniqueAssessmentItems(eligibility)
application = uniqueAssessmentItems(application)
notes = uniqueAssessmentItems(notes)
lead.set("match_strengths", strengths)
lead.set("fit_gaps", gaps)
lead.set("eligibility_constraints", eligibility)
lead.set("application_requirements", application)
lead.set("search_notes", notes)
lead.set("assessment_schema_version", 2)
return previousVersion < 2 || !hadStructuredAssessment
}
private enum AssessmentBucket: Equatable {
case application
case eligibility
case note
case gap
}
private func assessmentBucket(for value: String) -> AssessmentBucket {
let text = value.lowercased()
let applicationTerms = [
"transcript", "degree certificate", "diploma", "referee", "reference letter",
"merged application pdf", "application pdf", "upload", "online submission",
"application asks", "application form", "application is by email", "apply flow",
"fallback email", "cover letter", "motivation letter", "letter of motivation",
"curriculum vitae", "submit a cv", "requires a cv", "portfolio", "publication list",
"supporting document", "application material", "writing sample", "research proposal",
"statement of purpose", "academic record", "proof of degree", "deadline"
]
if applicationTerms.contains(where: text.contains) { return .application }
let noteTerms = [
"legacy package", "do not reuse", "keep as monitor", "manual review", "package deferred",
"human review", "not an automatic", "worth reviewing", "lower strategic priority",
"review project fit", "original job", "security checkpoint", "canonical lead",
"confirm the target job", "apply only if", "not a first-priority"
]
if noteTerms.contains(where: text.contains) { return .note }
let eligibilityTerms = [
"contract role", "fixed-term", "temporary", "maternity-cover", "maternity cover",
"on-site", "onsite", "relocation", "travel", "driving licence", "driver licence",
"driver's license", "munich-only", "full-time", "part-time", "six-month",
"work permit", "sponsorship", "german is", "german required", "french required",
"fluent german", "german proficiency", "german level", "fluent french",
"french proficiency", "french level", "italian required", "citizenship",
"appointment is 70 percent", "four on-site days"
]
if eligibilityTerms.contains(where: text.contains) { return .eligibility }
return .gap
}
private func assessmentClauses(_ value: String) -> [String] {
value
.replacingOccurrences(of: "; ", with: ";\n")
.replacingOccurrences(of: ". ", with: ".\n")
.split(separator: "\n")
.map(String.init)
}
private func cleanedAssessmentClause(_ value: String) -> String {
var result = value.trimmingCharacters(in: .whitespacesAndNewlines)
let prefixes = [
"Manual check before applying:",
"Manual check:",
"Stretch role:",
"High stretch:"
]
for prefix in prefixes where result.lowercased().hasPrefix(prefix.lowercased()) {
result = String(result.dropFirst(prefix.count)).trimmingCharacters(in: .whitespacesAndNewlines)
break
}
return result
}
private func uniqueAssessmentItems(_ values: [String]) -> [String] {
var seen = Set<String>()
return values.compactMap { value in
let cleaned = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard !cleaned.isEmpty else { return nil }
let key = cleaned.lowercased()
guard seen.insert(key).inserted else { return nil }
return cleaned
}
}
private func prefillProfileFromEvidenceBank() {
let url = workspaceURL.appendingPathComponent("Evidence_Bank/approved_evidence.json")
guard let data = try? Data(contentsOf: url),
let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let facts = root["profile_facts"] as? [String: Any] else { return }
config.profile.fullName = facts["name"] as? String ?? config.profile.fullName
config.profile.location = facts["location"] as? String ?? config.profile.location
config.profile.workAuthorization = facts["permit_line"] as? String ?? config.profile.workAuthorization
if let languages = facts["languages"] as? [String] {
config.profile.languages = languages.joined(separator: "; ")
}
if let masters = root["approved_master_cvs"] as? [String: String] {
config.cv.selectedMasterPaths = masters.keys.sorted().compactMap { masters[$0] }
}
}
private func detectExistingAutomation() {
let root = fileManager.homeDirectoryForCurrentUser
.appendingPathComponent(".codex/automations", isDirectory: true)
guard let directories = try? fileManager.contentsOfDirectory(
at: root,
includingPropertiesForKeys: [.contentModificationDateKey],
options: [.skipsHiddenFiles]
) else { return }
for directory in directories {
let file = directory.appendingPathComponent("automation.toml")
guard let text = try? String(contentsOf: file, encoding: .utf8),
text.contains(workspaceURL.path) else { continue }
let identifier = text
.split(separator: "\n")
.first { $0.trimmingCharacters(in: .whitespaces).hasPrefix("id =") }
.map(String.init)?
.split(separator: "=", maxSplits: 1)
.last?
.trimmingCharacters(in: CharacterSet(charactersIn: " \t\""))
if let identifier, !identifier.isEmpty {
config.automation.automationID = identifier
config.automation.needsCodexSync = true
config.automation.lastSyncedAt = ""
return
}
}
}
private func ensureWorkspaceStructure() {
let directories = [
"Applications",
"Automation",
"Config",
"Documents",
"Evidence_Bank",
"Job_Postings",
"Logs",
"Projects",
"State"
] + DocumentCategory.allCases.map { "Documents/\($0.rawValue)" }
do {
for path in directories {
try fileManager.createDirectory(
at: workspaceURL.appendingPathComponent(path, isDirectory: true),
withIntermediateDirectories: true
)
}
} catch {
errorMessage = "Could not prepare workspace: \(error.localizedDescription)"
}
}
private func documentDirectory(for category: DocumentCategory) -> URL {
workspaceURL.appendingPathComponent("Documents/\(category.rawValue)", isDirectory: true)
}
private func copyItemUniquely(from source: URL, to destination: URL) throws {
var candidate = destination.appendingPathComponent(source.lastPathComponent)
let stem = source.deletingPathExtension().lastPathComponent
let ext = source.pathExtension
var counter = 2
while fileManager.fileExists(atPath: candidate.path) {
let name = ext.isEmpty ? "\(stem) \(counter)" : "\(stem) \(counter).\(ext)"
candidate = destination.appendingPathComponent(name)
counter += 1
}
try fileManager.copyItem(at: source, to: candidate)
}
private func writeIntakeSummary() {
let lines = [
"# Career Command Center Intake",
"",
"Generated: \(Self.timestamp())",
"",
"## Candidate",
"- Name: \(config.profile.fullName)",
"- Location: \(config.profile.location)",
"- Work authorisation: \(config.profile.workAuthorization)",
"- Languages: \(config.profile.languages)",
"",
"## Search",
"- Countries: \(config.search.countries.joined(separator: ", "))",
"- Opportunity types: \(config.search.opportunityTypes.joined(separator: ", "))",
"- Work arrangements: \(config.search.workArrangements.joined(separator: ", "))",
"- Seniority: \(config.search.seniority.joined(separator: ", "))",
"- Target direction: \(config.search.targetRoleDescription)",
"- Role families: \(config.search.inferRoleFamilies ? "Infer from evidence" : config.search.roleFamilies.joined(separator: ", "))",
"- Include: \(config.search.includeKeywords)",
"- Exclude: \(config.search.excludeKeywords)",
"",
"## Evidence answers",
"### Education and experience",
config.evidence.educationAndExperience,
"",
"### Career direction",
config.evidence.careerDirection,
"",
"### Strongest work",
config.evidence.strongestWork,
"",
"### Ownership boundaries",
config.evidence.ownershipBoundaries,
"",
"### Verified metrics",
config.evidence.verifiedMetrics,
"",
"### Project context",
config.evidence.projectContext,
"",
"### Constraints and recurring concerns",
config.evidence.roleConcerns,
"",
"### Claims to avoid",
config.evidence.claimsToAvoid,
""
]
let url = workspaceURL.appendingPathComponent("Evidence_Bank/intake_answers.md")
do {
try lines.joined(separator: "\n").write(to: url, atomically: true, encoding: .utf8)
} catch {
errorMessage = "Could not save intake answers: \(error.localizedDescription)"
}
}
private func atomicWrite<T: Encodable>(_ value: T, to url: URL) throws {
try fileManager.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true)
let data = try encoder.encode(value)
try data.write(to: url, options: .atomic)
}
private static func resolveWorkspace() -> URL {
let arguments = CommandLine.arguments
if let index = arguments.firstIndex(of: "--workspace"), arguments.indices.contains(index + 1) {
return URL(fileURLWithPath: arguments[index + 1], isDirectory: true)
}
if let saved = UserDefaults.standard.string(forKey: workspacePreferenceKey), !saved.isEmpty {
return URL(fileURLWithPath: saved, isDirectory: true)
}
let home = FileManager.default.homeDirectoryForCurrentUser
return home.appendingPathComponent("Documents/Career Command Center", isDirectory: true)
}
static func timestamp() -> String {
ISO8601DateFormatter().string(from: Date())
}
}
extension AppStore {
static func previewConfig(workspaceURL: URL) -> AppConfig {
var config = AppConfig()
config.onboardingCompleted = true
config.workspacePath = workspaceURL.path
config.profile = CandidateProfile(
fullName: "Alex Morgan",
email: "alex@example.com",
phone: "+31 6 00000000",
location: "Rotterdam, Netherlands",
workAuthorization: "EU work authorisation recorded",
linkedinURL: "https://linkedin.com/in/alex",
githubURL: "https://github.com/alex",
languages: "English: fluent; Dutch: B2"
)
config.search = SearchPreferences(
countries: ["Netherlands", "Belgium"],
opportunityTypes: ["Job", "Graduate programme"],
workArrangements: ["On-site", "Hybrid"],
roleFamilies: ["Systems engineering", "Applied data analysis"],
inferRoleFamilies: false,
targetRoleDescription: "Evidence-led engineering and analytical roles",
seniority: ["Graduate", "Junior"],
includeKeywords: "",
excludeKeywords: "",
minimumScore: 82
)
return config
}
static func previewState() -> CommandCenterState {
let leads = [
LeadRecord(raw: [
"id": .string("preview-1"),
"title": .string("Systems Integration Engineer"),
"organization": .string("Atlas Instruments"),
"location": .string("Eindhoven, Netherlands"),
"type": .string("Job"),
"score": .integer(94),
"tier": .string("A"),
"status": .string("to_apply"),
"deadline": .string("28 July 2026"),
"match_strengths": .array([
.string("Structured validation work aligns with the system-integration responsibilities."),
.string("Python analysis and technical documentation support the role's engineering workflow.")
]),
"fit_gaps": .array([
.string("Formal ownership of product-certification work is not yet demonstrated.")
]),
"application_requirements": .array([
.string("Upload a CV, degree certificate, and transcript through the employer portal.")
]),
"assessment_schema_version": .integer(2),
"job_url": .string("https://example.com/job/1"),
"apply_url": .string("https://example.com/job/1/apply"),
"key_requirements": .array([
.string("MSc in mechanical, electrical, or systems engineering."),
.string("Hands-on integration, verification, and technical documentation."),
.string("Python or MATLAB for engineering analysis.")
]),
"job_summary": .array([
.string("Integrate and validate electromechanical subsystems for an industrial sensing platform.")
])
]),
LeadRecord(raw: [
"id": .string("preview-2"),
"title": .string("Graduate Data Analyst"),
"organization": .string("Civic Metrics Lab"),
"location": .string("Brussels, Belgium"),
"type": .string("Graduate programme"),
"score": .integer(91),
"tier": .string("A"),
"status": .string("to_apply"),
"match_strengths": .array([
.string("Python analysis, statistical modelling, and clear reporting support the core project work.")
]),
"assessment_schema_version": .integer(2),
"job_url": .string("https://example.com/job/2")
]),
LeadRecord(raw: [
"id": .string("preview-3"),
"title": .string("Product Development Engineer"),
"organization": .string("Northstar Manufacturing"),
"location": .string("Ghent, Belgium"),
"type": .string("Job"),
"score": .integer(87),
"tier": .string("B"),
"status": .string("monitor"),
"match_strengths": .array([
.string("CAD, prototyping, and test planning support the product-development workflow.")
]),
"assessment_schema_version": .integer(2),
"job_url": .string("https://example.com/job/3")
])
]
return CommandCenterState(leads: leads)
}
static func previewDocuments(workspaceURL: URL) -> [DocumentItem] {
[
DocumentItem(
url: workspaceURL.appendingPathComponent("Documents/CVs/Master_CV.pdf"),
category: .cvs,
modifiedAt: Date(),
byteCount: 410_000
),
DocumentItem(
url: workspaceURL.appendingPathComponent("Documents/Transcripts/MSc_Transcript.pdf"),
category: .transcripts,
modifiedAt: Date().addingTimeInterval(-3600),
byteCount: 280_000
)
]
}
static func previewQuestionBank() -> PersonalizedQuestionBank {
PersonalizedQuestionBank(
generationID: "preview-audit-1",
auditStatus: .current,
generatedAt: timestamp(),
questions: [
EvidenceQuestion(
id: "logitech-configuration-time-baseline",
priority: .critical,
category: .metric,
question: "The workflow report projects a 50% reduction in configuration time. Was that figure measured against a baseline, estimated from the process, or proposed as a target?",
whyItMatters: "The classification determines whether the CV can present the figure as a result, an estimate, or a design target.",
sourceRefs: [
EvidenceQuestionSource(
path: "Projects/Workflow Automation/Final Report.pdf",
label: "Workflow Automation Final Report",
locator: "Discussion, page 18",
context: "The report gives the 50% figure without identifying a measured baseline or validation run."
)
],
generatedAt: timestamp()
),
EvidenceQuestion(
id: "microfluidics-design-decision",
priority: .high,
category: .outcome,
question: "Which channel or pillar geometry did your final microfluidic analysis support, and what simulation result drove that choice?",
whyItMatters: "A concrete engineering decision would make the project evidence stronger than a description of the modelling workflow alone.",
sourceRefs: [
EvidenceQuestionSource(
path: "Projects/Microfluidics/Project Report.pdf",
label: "Capillary Microfluidics Project Report",
locator: "Results and conclusion, pages 21-24",
context: "Several geometries are compared, but the report does not clearly state the candidate's final design recommendation."
)
],
generatedAt: timestamp()
),
EvidenceQuestion(
id: "robot-gripper-personal-ownership",
priority: .high,
category: .ownership,
question: "For the robotic gripper, which CAD, fabrication, control, and testing tasks did you personally complete?",
whyItMatters: "The report uses team language, so individual ownership must be separated before project bullets are approved.",
sourceRefs: [
EvidenceQuestionSource(
path: "Projects/Robotic Gripper/Final Presentation.pdf",
label: "Robotic Gripper Final Presentation",
locator: "Design and testing slides 6-14",
context: "The presentation describes team outputs without assigning the main implementation tasks to individual members."
)
],
status: .answered,
answer: "I designed the finger linkage and servo mount in Fusion 360, prepared the PETG prints, integrated the Arduino servo control, and ran the object-grasp tests. The PMMA frame layout was shared.",
generatedAt: timestamp(),
answeredAt: timestamp()
)
]
)
}
}
SHA-256: 948c50b71c3efa78e1c301f19def9f6f75c35b72bfdbdc3608854338bc4276b4